随着工作年限的增长,越来越意识到C语言的重要性,Android的底层是C和linux内核,Android中为提高安全性,防止反编译,防止二次打包,提升程序的执行效率都是用C去实现的,作为Android开发者,掌握C语言才能进行NDK开发,提高自己的核心竞争力,拓宽职业道路
指针与地址
1 | int main(){ |
交换两个变量
1 | void change(int *a, int *b){ |
多级指针
- 指针变量存放的是内存地址,指针变量自己也有地址
1
2
3
4
5
6
7
8
9
10
11// 多级指针
void test1(){
int num = 999;
int * p1 = #
int ** p2 = &p1;
int *** p3 = &p2;
printf("p1的值是:%p, p2的值是:%p, p3的值是:%p\n", p1, p2, p3);// p1存放num内存地址,p2存放p1内存地址,p3存放p2内存地址
printf("p1的值是:%p, p2的值是:%p, p3的值是:%p\n", &p1, &p2, &p3);// p1,p2,p3自己的内存地址都不一样
printf("p2的内存地址对应的值是:%d\n",**p2); // 999
printf("p3的内存地址对应的值是:%d\n",***p3); // 999
}数组指针
- 指针变量在32位下占4个字节,64位下占8个字节,指针类型决定了sizeof,获取元素时的偏移
- 数组变量就是一个指针,存放的是第一个元素的内存地址,也等于它自己的内存地址
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22// 数组指针
void test2(){
int arr[] = {1,2,3,4};
int *arr_p = arr;
int i = 0;
for (i = 0; i < sizeof arr / sizeof(int); ++i){
printf("下标为%d的值是%d\n", i,*(arr_p + i));
printf("下标%d的内存地址是%p\n",i, arr_p + i); // 地址间隔4个字节
}
// 同一个地址
printf("arr = %p\n", arr);
printf("&arr = %p\n", &arr);
printf("&arr[0] = %p\n", &arr[0]);
// 取数组第二个值
arr_p ++;
printf("%d\n", *arr_p);
// 超出范围,野指针
arr_p += 200;
printf("%d\n", *arr_p);
}内存静态开辟和动态开辟
- 动态开辟需要手动释放,手动释放后如果不赋值为NULL,就是悬空指针
1
2
3
4
5
6
7
8
9
10
11
12
13void dynamicAction(int num){
// 堆区动态开辟1M内存
int *arr = malloc(num * sizeof(int));
printf("dynamicAction函数,arr自己的内存地址:%p,堆区开辟的内存地址:%p\n",&arr,arr);
// dynamicAction函数,arr自己的内存地址:0x7ffee47e1480,堆区开辟的内存地址:0x7fdb5dd00000
// 释放堆区开辟的内存
if(arr){
free(arr);
arr = NULL; // 如果不赋值为NULL,就是悬空指针
printf("dynamicAction函数2,arr自己的内存地址:%p,堆区开辟的内存地址:%p\n",&arr,arr);
// dynamicAction函数2,arr自己的内存地址:0x7ffee47e1480,堆区开辟的内存地址:0x0
}
} - 静态开辟,使用栈内存,自动释放
1
2
3
4void staticAction(){
int arr[6];
printf("staticAction函数,arr自己的内存地址:%p,堆区开辟的内存地址:%p\n",&arr,arr);
}realloc重新开辟内存
- 扩容内存时,地址不一定连续,物理内存被其它占用会返回新的地址,所以内存重新开辟时需要传入指针和总大小进行拷贝
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35int main(){
int num;
printf("请输入新的个数:");
// 获取用户输入的值
scanf("%d", &num);
int *arr = (int *)malloc(arr, sizeof(int) * num);
for (int i = 0; i < num; ++i){
arr[i] = i + 10001;
}
printf("开辟的内存地址:%p\n", arr);
// 开辟新的内存空间
int newNum;
printf("请输入新增加的个数");
scanf("%d", &newNum);
int *newArr = (int *)realloc(arr, sizeof(int) * (num + newNum));
if (newArr){
int j = num;
for (;j < (num + newNum); ++j){
arr[j] = j + 10001;
}
printf("新开辟的内存地址:%p\n", newArr);
}
// 释放内存操作
if (newArr){
free(newArr);
newArr = NULL;
arr = NULL;
}
else{
free(arr);
arr = NULL;
}
return 0;
}函数指针
- 使用函数指针实现回调,相当于Java的接口
- 函数指针和它自己的内存地址相同
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20void add(int num1, int num2){
printf("num1 + num2 = %d\n", (num1 + num2));
}
void mins(int num1, int num2){
printf("num1 - num2 = %d\n", (num1 - num2));
}
// 传递函数指针
void operate(void(*method) (int,int),int num1, int num2){
method(num1,num2);
}
void test4(){
operate(add, 10, 20);
void(*method)(int,int) = mins;
operate(method, 100, 20);
// 函数指针和它自己的内存地址相同
printf("%p, %p\n", add, &add);
}生成随机数
1
2
3
4
5
6
7
8
9
void test5(){
srand((unsigned) time(NULL));
int i;
for (i = 0; i < 10; ++i) {
printf("随机数%d\n", rand() % 100);
}
}复制字符串
1
2
3
4
5
6
7
void test6(){
char string[10];
char* str1 = "abcdefghi";
strcpy(string, str1);
printf("%s\n", string);
}字符串获取长度
- 也可以直接使用strLen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26void getLen(int *resultLen, char *str){
int count = 0;
while(*str){
str ++;
count ++;
}
*resultLen = count;
}
/**
* C/C++会把数组优化成指针提高效率,导致长度计算错误
int getLen(int arr[]){
int len = sizeof(arr) / sizeof(int);
return len;
}
*/
void test7{
char str1[] = {'H','e','l','l','o','\0'}; // 遇到\0停下来
str1[2] = 'z'; // 栈空间,允许修改
printf("第一种方式:%s\n", str1);
int count;
getLen(&count, str1);
printf("长度为:%d\n", count);
char *str2 = "Hello"; // 结尾隐式增加\0
// str2[2] = 'z'; 会报错,不允许修改全局区的字符串
printf("第二种方式:%s\n", str2);
}字符串转换
1
2
3
4
5
6
7
8
9
10void convertInt(){
char *num = "1";
int res = atoi(num);
printf("转换结果:%d\n", res);
}
void convertDouble(){
double resD = atof(num);
printf("转换结果:%lf\n", resD);
}字符串比较
1
2
3
4
5
6
7
8
9
10
11void test8(){
char *str1 = "Hello";
char *str2 = "hello";
// int res = strcmp(str1, str2); // 区分大小写
int res = strcmpi(str1, str2); // 不区分大小写
if (!res){
printf("相等");
}else{
printf("不相等")
}
}字符串查找子串
1
2
3
4
5
6
7
8
9
10
11
12
13
14void test9(){
char *text = "hello world";
char *subtext = "w";
// 从搜索到子串的下标位置截取到末尾
char *pop = strstr(text, subtext);
if (pop){
printf("查找到了,pop的值是%s\n",pop);
}
else{
printf("没有查找到,subtext的值是%s\n",subtext);
}
int index = pop - text;
printf("%s第一次出现的位置是:%d\n",subtext, index);
}字符串拼接
1
2
3
4
5
6
7
8void test10(){
char destination[25];
char *blank = "--到--", *CPP = "C++", *Java = "Java";
strcpy(destination, CPP); // 先拷贝到数组
strcat(destination, blank); // 拼接blank
strcat(destination, Java); // 拼接Java
printf("拼接后的结果%s\n", destination);
}字符串截取
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41void substring1(char *res, char *str, int start, int end){
char *temp = str;
int index = 0; // 当前截取的位置
while(*temp){
if (index > start && index < end){
*res = *temp;
res ++;
}
temp ++;
index ++;
}
}
void substring2(char **res, char *str, int start, int end){
char *temp = str;
char resArr[end - start]; // 方案1. 临时变量在栈中分配内存,方法结束会被释放
// char *resArr = malloc(end - start); // 方案2. 堆中开辟内存
int index = 0;
for (int i = start, i < end; ++i){
resArr[index] = *(temp + i);
index ++;
}
// 二级指针的一级指针等于test11的res一级指针
// *res = resArr; // 方案2. 结果指向堆中的数组,方法结束后也不能释放所以不推荐
strcpy(*res, resArr); // 方案1. 拷贝到数组
printf("截取后的结果:%s\n", resArr);
}
void substring3(char *res, char *str, int start, int end){
for (int i = start; i < end; ++i){
*(result++) = *(str + i);
}
}
void substring4(char *res, char *str, int start, int end){
strncpy(result, str + start, end - start);
}
void test11(){
char *str = "hello";
char *res;
substring1(res, str, 1, 4);
// substring2(&res, str, 1, 4);
printf("截取后的内容是:%s",res);
}大小写转换
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16void lower(char *dest, char *text){
int *temp = text;
while(*temp){
*dest = tolower(*temp);
temp ++;
dest ++;
}
*dest = '\0'; // 结尾增加\0避免打出系统值
printf("name:%s\n", text);
}
void test12(){
char *text = "hello";
char dest[20];
lower(dest, text);
printf("小写转换后的结构是:%s\n", dest);
}结构体
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82struct Dog{
char name[10];
int age;
char sex;
}
struct Person{
char *name;
int age;
char sex;
} person1 = {"小明", 21, 'M'}
struct Study{
char *studyContent;
}
struct Student{
char name[10];
int age;
char sex;
struct Study study; // 引用外部结构体,声明结构体对象
// 定义结构体并声明结构体对象
struct Wan {
char *wanContent;
}wan;
}
// 匿名结构体定义别名
typedef struct {
char *name;
int age;
char sex;
} Cat;
int main(){
struct Dog dog;
strcpy(dog.name, "旺财");
dog.age = 2;
dog.sex = 'M';
printf("name:%s, age:%d, sex:%c \n", dog.name, dog.age, dog.sex);
struct Student student = {"小红", 18, 'F', {"学习C"}, {"王者荣耀"}};
printf("name:%s, age:%d, sex:%c, study:%s, wan:%s \n", student.name, student.age, student.sex, student.study.studyContent, student.wan.wanContent);
struct Dog dog2 = {"旺财2",4, 'M'};
struct Dog *dogp = &dog2;
dogp -> age = 3;
dogp -> sex = 'F';
strcpy(dogp->name, "旺财3");
printf("name:%s, age:%d, sex:%c \n", dogp->name, dogp->age, dogp->sex);
free(dogp);
dogp = NULL;
struct Dog dogArr[10] = {
{"旺财4",4, 'M'},
{"旺财5",5, 'M'},
{"旺财6",6, 'M'},
{},
{},
{},
{},
{},
{},
{}
};
struct Dog dog9 = {"旺财9",9, 'M'};
// dogArr[9] = dog9;
*(dogArr + 9) = dog9;
printf("name:%s, age:%d, sex:%c \n", dog9.name, dog9.age, dog9.sex
// 动态申请内存
struct Dog *dogArr2 = malloc(sizeof(struct Dog) * 10);
strcpy(dogArr2->name, "大黄1");
dogArr2 -> age = 2;
dogArr2 -> sex = 'M';
printf("name:%s, age:%d, sex:%c \n", dogArr2->name, dogArr2->age, dogArr2->sex);
// 指针移到第8个元素
dogArr2 += 7;
strcpy(dogArr2 -> name, "大黄8");
dogArr2 -> age = 3;
dogArr2 -> sex = 'M';
printf("name:%s, age:%d, sex:%c \n", dogArr2->name, dogArr2->age, dogArr2->sex);
free(dogArr2);
dogArr2 = NULL;
Cat *cat = malloc(sizeof(Cat)); // 结构体指针
return 0;
}枚举
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18enum CommentType{
TEXT = 10,
TEXT_IMAGE,
IMAGE
};
typedef enum {
TEXT1 = 10,
TEXT_IMAGE1,
IMAGE1
} CommentType1;
int main(){
enum CommentType commentType = TEXT;
printf("%d\n", commentType);
CommentType1 commentType1 = TEXT1;
printf("%d\n", commentType1);
return 0;
}文件读写
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26void readFile(){
char *fileName = "/Users/JessieKate/CLionProjects/TestProject/test.txt";
FILE *file = fopen(fileName,"r"); // 此文件必须存在
if (!file){
printf("文件打开失败,请查看路径");
exit(0);
}
char buffer[10]; // 创建buffer读取
while(fgets(buffer, 10, file)){
printf("%s", buffer);
}
// 关闭文件
fclose(file);
}
void writeFile(){
char *fileName = "/Users/JessieKate/CLionProjects/TestProject/test1.txt";
FILE *file = fopen(fileName, "w"); // 此文件可以不存在
if (!file){
printf("文件打开失败,请查看路径");
exit(0);
}
fputs("这是我写入的测试内容", file);
fclose(file);
}文件复制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20void copyFile(){
char *fileName = "/Users/JessieKate/CLionProjects/TestProject/test.txt";
char *fileNameCopy = "/Users/JessieKate/CLionProjects/TestProject/testCopy.txt";
FILE *file = fopen(fileName, "rb");
FILE *fileCopy = fopen(fileNameCopy, "wb");
if (!file || !fileCopy){
printf("文件打开失败,请查看路径");
exit(0);
}
int buffer[514]; // 缓存数组
int len; // 每次读取的长度
// fread 读入缓存buffer, 偏移数量, 读取字节数写入缓存
while((len = fread(buffer, 1, 514, file)) > 0){
fwrite(buffer, len, 1, fileCopy);
}
fclose(file);
fclose(fileCopy);
}文件大小
1
2
3
4
5
6
7
8
9
10
11void getSize(){
char *fileName = "/Users/JessieKate/CLionProjects/TestProject/test.txt";
FILE *file = fopen(fileName,"r");
if (!file){
printf("文件打开失败,请查看路径");
exit(0);
}
fseek(file, 0, SEEK_END); //从0开始挪动到文件结束
long fileSize = ftell(file); //读取file的信息
printf("%s文件的字节大小是:%ld",fileName, fileSize);
}文件加密解密
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51void encrypt(){
char * fileName = "/Users/JessieKate/CLionProjects/TestProject/Image.jpg";
char * fileNameEncode = "/Users/JessieKate/CLionProjects/TestProject/Image_encode.jpg";
FILE * file = fopen(fileName, "rb");
FILE * fileEncode = fopen(fileNameEncode,"wb");
if (!file || !fileEncode){
printf("文件打开失败,请查看路径");
exit(0);
}
char *password = "123456";
// 加密,破坏文件,解密,还原文件
int c;
int index = 0;
int pass_len = strlen(password); // 获取密码的长度
// fgetc 返回EOF = end of file
while((c = fgetc(file)) != EOF){
// 循环获取密码的每个字符,1,2,3,4,5,6,1,2,3...
char item = password[index % pass_len];
printf("item:%c%\n",item);
fputc( c ^ item, fileEncode);
index++;
}
fclose(file);
fclose(fileEncode);
}
void decrypt(){
char * fileNameEncode = "/Users/JessieKate/CLionProjects/TestProject/Image_encode.jpg";
char * fileNameDecode = "/Users/JessieKate/CLionProjects/TestProject/Image_decode.jpg";
FILE * file = fopen(fileNameEncode, "rb");
FILE * fileDecode = fopen(fileNameDecode,"wb");
if (!file || !fileDecode){
printf("文件打开失败,请查看路径");
exit(0);
}
char *password = "123456";
int c;
int index = 0;
int pass_len = strlen(password);
// fgetc 返回EOF = end of file
while((c = fgetc(file)) != EOF){
char item = password[index % pass_len];
fputc( c ^ item, fileDecode);
index++;
}
fclose(file);
fclose(fileDecode);
}