NDK学习笔记-C语言
本文简要回顾了C语言的一些注意事项和理解细节,不再赘述C语言的所有语法
头文件
头文件作为引入文件,在编译的时候,加载到源代码,参与编译
在VS2013中可以看到,当引入头文件时候,只能看到函数的声明,其实现是在编译时候查找的
C的动态库函数不可重名,而C++可以,这是因为C++有命名空间的存在,而C没有
//引入头文件
#include <stdio.h>
void main()
{
printf("%s", "test");
}
基本数据类型
C中的基本数据类型包括:int, char, short, long, float, double
值得注意的是,在C语言中,char代表一个字节,而在Java中,char代表两个字节,在Java中与C的char对应的基本数据类型为byte,这个数据类型在C中是不存在的
基本数据类型常用的打印标识(与上述基本类型一一对应):%d, %c, %d, %ld, %f, %lf
%x – 十六进制小写
%X – 十六进制大写
%o – 八进制
%s – 字符串
sizeof关键字
在C语言中,sizeof作为关键字存在,而不是一个函数
使用sizeof()可以获得一个数据类型所占字节数的大小,例如:sizeof(char)
循环的注意事项
for循环的初始化值一般要在循环外定义,这样的原因是因为C89的规范,在很多发行版的Linux中,仍然采用的是C89的C语言规范,以下语句在很多Linux发行版无法编译通过
for(int i = 0; i < 10; i++){}
VS中使用scanf,gets等函数的注意事项
在VS中,像scanf,gets这样的函数被定义为不安全函数
要使用这些函数,就需要将其编译错误去掉,常用的方法有三种
- 添加宏定义,生效于单个文件
#define _CRT_SECURE_NO_WARNINGS
- 在设置中去除,生效于整个项目
项目->属性->配置属性->C/C++ -> 预处理器 -> 预处理器定义,增加:_CRT_SECURE_NO_DEPRECATE - 去除编译时的错误
#param warning(disable:4996)
- 使用VS自定义的函数
scanf_s("%s", buf);
以上三种方法都可以使编译通过,但都有不足之处,其最大的问题在与跨平台编译,相对来说第二种方法稍好,在Linux平台下编译的话,不用处理源代码便可以
VS中查看内存
在调试的时候可以查看内存在变化情况,这是VS最好用的功能之一
要使用内存查看,在程序调试的时候就需要存在断点
其步骤为:加断点 -> Debug模式 -> 调试 -> 窗口 -> 内存
然后按照内存地址,便可以查看到内存变化情况
dll相关
在一个exe程序中是不可以修改另一个exe程序的,要实现这一操作,就需要dll实现,这也是外挂的基本原理
生成dll的步骤为:项目 -> 属性 -> 常规 -> 配置类型:dll -> 生成解决方案
这样,一个dll就生成成功了,在dll中的函数需要用相关标识导出,这样才能被运用
__declspec(dllexport) void go()
{
int *p = 0x0011dd;
*p = 100;
}
指针的一些相关知识
指针存储的是内存地址
int i = 0;
int *p = &i;
无论何种类型的指针,其大小都是一样的
指针之所以要有数据类型,是因为指针取值的时候要知道其读取规则,指针得到一个内存地址,知道了存储的位置,但却不知道需要读取的长度,此时数据类型就指明了指针需要读取的长度
空指针:*p = NULL;,其指向地址为零的位置
多级指针:指针存储的是地址,而指针变量也有地址,也就是说,指针可以存储指针,这就是多级指针
指针的运算:一般只有在数组遍历的时候才有意义,这是由于数组的顺序排列导致的
数组简要说明
arrayName <=> &arrayName <=> &arrayName[0]
数组长度:sizeof(arr) / sizeof(type)
arr[i][j] <=> *(*(a + i) + j)
&arr[i][j] <=> (*(a + i) + j)
数组的[]在编译器底层做了类似于重定义的操作
int arr[5];
int i = 0;
for(; i < 5; i ++)
{
arr[i] = i;
}
而在[]符号出现以前,数组的操作是基于指针的
int arr[5];
int *p = arr;
int i = 0;
for(; p < arr + 5; p++)
{
*p = i;
i++;
}
当在栈中定义数组,数组定义过大时,会造成栈内存溢出
在Windows下,栈内存的大小为2M
函数指针
函数指针在NDK开发中有着大量运用,是重点内容
函数指针实例:
int add(int a, int b)
{
return a + b;
}
int minus(int a, int b)
{
return a - b;
}
void test(int(*func_p)(int a, int b), int m, int n)
{
int x = func_p(m, n);
printf("%d\n", x);
}
void main()
{
test(add, 1, 2); //执行加法运算
test(minus, 2, 1); //执行减法运算
getchar();
}
生成随机数
在C语言中,生成随机数是很重要的一个运用
srand((unsigned)time(NULL)); //time为随机数种子,如果没有这一步,生成的随机数一直固定
rand(); //此步骤生成随机数
C语言的内存划分
C语言中,对内存进行了抽象的划分,而这些划分在正是内存中是不存在的
栈区(stack),堆区(heap),全局区或静态区,字符常量区,程序代码区
栈内存自动释放,对内存手动释放
有了这些只是以后,要创建大型数组时候,就可以在堆内存中创建了
静态内存分配创建数组,数组的大小在创建的时候便已经固定:
int a[10]; //(此处不考虑C99才有的变长数组)
动态分配内存:
int *p = malloc(len * sizeof(int)); //其含义是创建一块大小为len*sizeof(int)大小的堆内存
//要操作可以使用p[i]
free(p)
在堆内存中开辟的空间,在使用完毕,必须使用free释放,否则会造成内存溢出
if(p != NULL)
{
free(p);
p = NULL;
}
一般使用malloc开辟的内存,需要用memset进行初始化,而使用calloc分配的内存已经初始化过了
如果分配的内存不够用,此时就需要使用realloc重新分配内存
int *p2 = realloc(p, sizeof(int) * (len + addlen));
重新分配内存,可能出现如下问题
- 缩小:缩小的那一部分消失
- 扩大
- 若后面有足够的空间,扩展并返回原指针
- 若后面空间不足,指到新空间,并将原有的值复制过去,清除现有数据并返回新地址
- 如果申请失败,返回NULL,原来指针仍然有效
字符的修改问题
- 字符数组存储在字符串中,那么可以被修改
char str[] = {'c','h','i','n','a','\0'};
char str[6] = {'c','h','i','n','a'};
char str[10] = "china";
str[0] = 's'; //可以修改的本质在于字符数组存在于栈内存中
- 字符指针,不可修改
char *p = "china";
p[0] = 's'; //此时会报错,因为此时的字符存储在字符常量区,不可被修改
- 字符操作的一些常用函数:
strcpy,strcat,strchr,strstr,strcmp等,此处不再一一赘述,详细用法可参考C标准库-string.h以及字符串函数
结构体相关内容
结构体也是一种数据类型
struct Man
{
char name[64];
int age;
};
初始化结构体变量
//方法一
struct Man m1 = {"jack", 20};
//方法二
struct Man m2;
strcpy(m2.name, "jack");
m2.age = 20;
当结构体中存在指针的时候,需要使用strcpy进行赋值,否则会造成指针指向内容被释放,产生野指针的情况
结构体的其他写法:
struct Man
{
char *name;
int age;
}m1; //结构体变量名
struct Man
{
char *name;
int age;
}m1,m2 = {"jack", 20};
匿名结构体:用于控制结构体变量的个数,相当于单例
struct
{
char *name;
int age;
}m1;
结构体中允许嵌套结构体
结构体与指针
struct Man m1 = {"jack", 20};
struct Man *p = &m1;
//m1.name m1.age
//(*p).name (*p).age
//p->name p->age
结构体大小(字节对齐)
结构体的动态内存分配
struct Man *p_m = (struct Man *)malloc(sizeof(struct Man) * 10);
struct Man *p = p_m;
p->name = "jack";
p->age = 20;
p++;
p->name = "alen";
p->age = 19;
···
free(p_m);
p_m = NULL;
typedef类型取别名
typedef int jint;
typedef _JNIEnv JNIEnv;
typedef _JavaVM JavaVM;
结构体取别名
typedef struct _Man
{
char name[64];
int age;
}Man,*ManP; //结构体别名和结构体指针别名,二者不存在必然联系,除非建立关联ManP = &Man
结构体的函数指针成员
struct Girl
{
char *name;
int age;
void (*sayHi)(char *);
}
void sayHi(char* text){}
void main()
{
struct Girl girl;
girl.name = "Lucy";
girl.age = 18;
girl.sayHi = sayHi;
girl.sayHi("Hi");
}
联合体
不同类型的变量共同占用一段内存,任何时候只有一个成员
联合体的大小等于最大成员所占的字节数
union my_value{
int x;
int y;
double z;
};
void main(){
union my_value d1;
d1.x = 90;
d1.y = 100; //最后一次赋值有效
printf("%d,%d,%lf\n", d1.x, d1.y, d1.z);
system("pause");
}
枚举
列举所有情况
限定值,保证数据的安全性
enum day
{
Monday,
Tuesday = 2, //此时,Tuesday为2,下一个在没有指定的情况下为3
Wednesday,
Thursday,
Friday,
Saturday,
Sunday
};
void main(){
enum day d = Monday;
printf("%d\n", d);
getchar();
}
可以任意指定位置
枚举的值必须为所列举的值
文本操作
读取文本文件
char *path = "C:\\a.txt"; //路径
FILE *fp = fopen(path, "r");
if(fp == NULL)
{
printf("文件打开失败");
return;
}
char buf[64] = { 0 }; //缓冲
while(fgets(buf, 64, fp))
{
printf("%s", buf);
}
fclose(fp); //关闭
写入文本文件
char *path = "C:\\b.txt";
FILE *fp = fopen(path, "w");
char *text = "test text";
fputs(text, fp);
fclose(fp);
逻辑:路径 -> 打开 -> 读取/写入 -> 关闭
c读写文本文件和二进制文件进体现在回车换行符
- 写文本时,遇到"
\n",转化为"\r\n" - 度文本时,遇到"
\r\n",转化为"\n"
文件复制
char *read_path = "C:\\test.png";
char *write_path = "C:\\test_copy.png";
FILE *read_fp = fopen(read_path, "rb");
FILE *write_fp = fopen(write_path, "wb");
int buf[64] = 0;
int len = 0;
while(len = fread(buf, sizeof(int), 64, read_fp) != 0)
{
fwrite(buf, sizeof(int), len, write_fp);
}
fclose(read_fp);
fclose(write_fp);
获取文件大小
char *read_path = "C:\\test.png";
FILE *fp = fopen(read_path, "r");
fseek(fp, 0, SEEK_END); //SEEK_END文件末尾,0偏移量
long filesize = ftell(fp); //返回当前文件指针,相对于文件开头的位置
文本文件加解密
//加密
void crypt(char *normal_path, char *crypt_path)
{
FILE *normal_fp = fopen(normal_path, "r");
FILE *crypt_fp = fopen(crypt_path, "w");
int ch = 0; //一次读取一个字符
while((ch = fgetc(normal_fp)) != EOF)
{
fputc(ch ^ 8, crypt_fp);
}
fclose(mormal_fp);
fclose(crypt_fp);
}
//解密
void decrypt(char *crypt_path, char *decrypt_path)
{
FILE *crypt_fp = fopen(crypt_path, "r");
FILE *decrypt_fp = fopen(decrypt_path, "w");
int ch = 0;
while((ch = fgetc(crypt_fp)) != EOF)
{
fputc(ch ^ 8, decrypt_fp);
}
fclose(crypt_fp);
fclose(decrypt_fp);
}
二进制文件加解密
和文本文件类似,不过是在读取和写入时候采用"rb"与"wb"
void crypt(char *normal_path, char *crypt_path)
{
FILE *normal_fp = fopen(normal_path, "rb");
FILE *crypt_fp = fopen(crypt_path, "wb");
int ch = 0; //一次读取一个字符
while((ch = fgetc(normal_fp)) != EOF)
{
fputc(ch ^ 8, crypt_fp);
}
fclose(mormal_fp);
fclose(crypt_fp);
}
日志输出
__VA_ARGS__可变参数
#define LOG(FORMAT,...) printf(##FORMAT,__VA_ARGS__);
日志区分级别
#define LOG_I(FORMAT,...) printf("INFO:"); printf(##FORMAT,__VA_ARGS__);
#define LOG_I(FORMAT,...) printf("ERRO:"); printf(##FORMAT,__VA_ARGS__);
#define LOG(LEVEL,FORMAT,...) printf("##LEVEL"); printf(##FORMAT,__VA_ARGS__);
#define LOG_I(FORMAT,...) LOG("INFO:",##FORMAT,__VA_ARGS__);
#define LOG_W(FORMAT,...) LOG("WARN:",##FORMAT,__VA_ARGS__);
#define LOG_E(FORMAT,...) LOG("ERRO:",##FORMAT,__VA_ARGS__);
Android下的LOG定义
#define LOGI(FORMAT,...) __android_log_print(ANDROID_LOG_INFO,"jack",FORMAT,##__VA_ARGS__);
LOGI("%s", "test");
__android_log_print(ANDROID_LOG_INFO,"jack","%S","test");
NDK学习笔记-C语言的更多相关文章
- [java学习笔记]java语言核心----面向对象之this关键字
一.this关键字 体现:当成员变量和函数的局部变量重名时,可以使用this关键字来区别:在构造函数中调用其它构造函数 原理: 代表的是当前对象. this就是所在函数 ...
- [java学习笔记]java语言核心----面向对象之构造函数
1.构造函数概念 特点: 函数名与类名相同 不用定义返回值类型 没有具体的返回值 作用: 给对象进行初始化 注意: 默认构造函数 多个构造函数是以重载出现的 一个类中如果 ...
- Java学习笔记:语言基础
Java学习笔记:语言基础 2014-1-31 最近开始学习Java,目的倒不在于想深入的掌握Java开发,而是想了解Java的基本语法,可以阅读Java源代码,从而拓展一些知识面.同时为学习An ...
- IOS学习笔记07---C语言函数-printf函数
IOS学习笔记07---C语言函数-printf函数 0 7.C语言5-printf函数 ------------------------- ----------------------------- ...
- IOS学习笔记06---C语言函数
IOS学习笔记06---C语言函数 -------------------------------------------- qq交流群:创梦技术交流群:251572072 ...
- ios开发学习笔记001-C语言基础知识
先来学习一下C语言基础知识,总结如下: 在xcode下编写代码. 1.编写代码 2.编译:cc –c 文件名.c 编译成功会生成一个 .o的目标文件 3.链接:把目标文件.o和系统自带的库合并在一起, ...
- ndk学习之C语言基础复习----虚拟内存布局与malloc申请
在这一次中来学习一下C语言的内存布局,了解它之后就可以解释为啥在用malloc()申请的内存之后需要用memset()来对内存进行一下初始化了,首先来了解一下物理内存与虚拟内存: 物理内存:通过物理内 ...
- ndk学习之C语言基础复习----基本数据类型、数组
关于NDK这个分类在N年前就已经创建了,但是一直木有系统的记录其学习过程,当然也没真正学会NDK的技术真谛,所以一直也是自己的一个遗憾,而如今对于Android程序员的要求也是越来越高,对于NDK也是 ...
- iOS学习笔记---oc语言第一天
第一讲 初始类和对象 c语言的超集,允许在oc中使用c语言源代码.编译器兼容c语言程序 具备完善的面向对象特性 包含一个运行时系统 类库丰富 面向对象编程 oop 面向对象语言:c++ java ...
随机推荐
- Git始终忽略特定文件的某一行内容
笔者在编写Z Shell文件的时候经常会使用到set -x来开启调试,但不希望提交到仓库 解决方案 Git提供了一种文件过滤器,此方法可以帮助在提交时移除set -x 我们先来编写脚本,如何移除这一行 ...
- oracle汉字排序
oracle在9i之前是对汉字的排序是按照二进制编码进行排序的,很不适合我们的国情,在oracle9i之后,汉字的排序方式有了以下三种方式: 1.使用拼音排序 NLS_SORT=SC ...
- 在当前目录下配置ansible
配置ansible.cfg [defaults] inventory = myhost # 指定主机清单文件 host_key_checking = False 配置主机清单文件 [node] nod ...
- Cogs 604.方程(排列组合+高精度)
方程 ★☆ 输入文件:equationz.in 输出文件:equationz.out 简单对比 时间限制:1 s 内存限制:128 MB [题目描述] hyc 碰到了一个难题,请你来帮忙解决. 对于不 ...
- 【原创】洛谷 LUOGU P3373 【模板】线段树2
P3373 [模板]线段树 2 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.将某区间每一个数乘上x 3.求出某区间每一个数的和 输入输出格式 输入格式: 第 ...
- codeforces316E3
Summer Homework CodeForces - 316E3 By the age of three Smart Beaver mastered all arithmetic operatio ...
- 掌握 3 个搜索技巧,在 GitHub 上快速找到实用软件资源
GitHub 作为目前广大程序猿最大的游乐场,在今年 6 月被 微软 以 75 亿美元价值的微软股票收购,GitHub 再次成为业界讨论的焦点.GitHub 以自由开放的定位吸引了相当多的个人开发者和 ...
- js切换全屏
直接撸代码 //<a id="fullscreen">切换按钮</a> $('#fullscreen').bind('click',function () ...
- [NLP-CNN] Convolutional Neural Networks for Sentence Classification -2014-EMNLP
1. Overview 本文将CNN用于句子分类任务 (1) 使用静态vector + CNN即可取得很好的效果:=> 这表明预训练的vector是universal的特征提取器,可以被用于多种 ...
- 预处理、const、static与sizeof-为什么要引入内联函数
1:引入内联函数的主要目的是,用它替代C语言中表达形式的宏定义来解决程序中函数调用的效率问题.在C语言里可以使用如下的宏定义: #define ExpressionName(Var1,Var2) (V ...