0%

死磕C语言


随着工作年限的增长,越来越意识到C语言的重要性,Android的底层是C和linux内核,Android中为提高安全性,防止反编译,防止二次打包,提升程序的执行效率都是用C去实现的,作为Android开发者,掌握C语言才能进行NDK开发,提高自己的核心竞争力,拓宽职业道路


指针与地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int main(){
int i = 100;
double d = 200;
printf("i的值是:%d\n", i); // 100
printf("d的值是:%lf\n", d); // 200

printf("i的值是:%d\n", *(&i)); // 100 取该地址的值
printf("d的值是:%lf\n", *(&d)); // 200 取该地址的值

int *intP = &i;
double *doubleP = &d;

*intP = 220; // 修改内存地址对应的值为220
printf("i的值是:%d\n", *intP); // 220 取该地址的值
printf("d的值是:%lf\n", *doubleP); // 200 取该地址的值
}

交换两个变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void change(int *a, int *b){
int temp = *a;
*a = *b;
*b = temp;
}
int main(){
int a = 100;
int b = 200;
printf("a的值为:%d\n", a); // 100
printf("b的值为:%d\n", b); // 200

change(&a, &b);

printf("a的值为:%d\n", a); // 200
printf("b的值为:%d\n", b); // 100
return 0;
}

多级指针

  • 指针变量存放的是内存地址,指针变量自己也有地址
    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
    13
    void 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
    4
    void 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
    35
    int 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
    20
    void 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
    #include <stdlib.h>
    #include <time.h>
    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
    #include <string.h>
    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
    26
    void 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
    10
    void 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
    11
    void 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
    14
    void 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
    8
    void 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
    41
    void 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
    16
    void 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
    82
    struct 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
    18
    enum 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
    26
    void 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
    20
    void 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
    11
    void 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
    51
    void 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);
    }