• C语言 指针进阶


    目录

    数组指针

    指针数组访问数组元素

    再次讨论数组名

    数组指针访问一维数组(但是这样会很别扭)

    访问二维数组元素

    非数组指针访问

    数组指针访问

     数组传参Demo

    一维数组传参

    二维数组传参

    指针数组指针

    字符指针

    函数指针

    函数指针调用时可以不用写*解引用

    函数作为回调传参

    解释: (*(void (*)())0)();

    解释: void (*signal(int, void (*)(int)))(int);

    函数指针数组指针


     

    数组指针

    数组指针——>指向数组的指针

    1. int ary[10] = {0};
    2. int p = ary;
    3. int *p1[10]; // p1是指针数组
    4. int(*p2)[10] = &ary; // p2是数组指针,(&ary取的是整个数组的地址)p2可以指向一个数组,该数组有10个元素,每个元素是int类型,数组指针的类型是int (*)[10]

    指针数组访问数组元素

    1. int arr1[] = {1, 2, 3, 4, 5};
    2. int arr2[] = {2, 3, 4, 5, 6};
    3. int arr3[] = {3, 4, 5, 6, 7};
    4. int *parr[3] = {arr1, arr2, arr3}; // 指针数组
    5. for (int i = 0; i < 3; i++)
    6. {
    7. for (int j = 0; j < 5; j++)
    8. {
    9. /* *(p+i) ——> p[i] */
    10. // printf("%d ", *(parr[i] + j));
    11. printf("%d ", parr[i][j]);
    12. }
    13. printf("\n");
    14. }

    再次讨论数组名

    数组名通常表示的都是数组首元素的地址
    但是有2个例外:
       1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小
       2. &数组名,这里的数组名表示的依然是整个数组,所以&数组名取出的是整个数组的地址

     例

    1. int arr[10] = {0};
    2. printf("%p\n", arr); // 0000003199fff960
    3. printf("%p\n", arr + 1); // 0000003199fff964 加4个字节
    4. printf("%p\n", &arr[0]); // 0000003199fff960
    5. printf("%p\n", &arr[0] + 1); // 0000003199fff964 加4个字节
    6. printf("%p\n", &arr); // 0000003199fff960
    7. printf("%p\n", &arr + 1); // 0000003199fff988 加40个字节

    数组指针访问一维数组(但是这样会很别扭)

    1. int ARR[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    2. int(*P)[10] = &ARR;
    3. int SZ = sizeof(ARR) / sizeof(ARR[0]);
    4. for (int i = 0; i < SZ; i++)
    5. {
    6. /* 1 2 3 4 5 6 7 8 9 10 */
    7. printf("%d", *(*P + i)); // P指向数组的人,*P其实就相当于数组名,数组名又是数组首元素的地址。所以*p本质上就是数组首元素的地址
    8. }
    9. printf("\n");

    思考一下:与其 int(*P)[10] = &ARR 这么麻烦,为什么不直接使用 int *p = arr 呢?这是因为数组指针的更适合和二维数组相结合,如下

    访问二维数组元素

    非数组指针访问

    1. int ARRAY[3][5] = {
    2. 1, 2, 3, 4, 5,
    3. 2, 3, 4, 5, 6,
    4. 3, 4, 5, 6, 7};
    5. void printf1(int arr[3][5], int r, int c)
    6. {
    7. for (int i = 0; i < r; i++)
    8. {
    9. for (int j = 0; j < c; j++)
    10. {
    11. printf("%d", arr[i][j]);
    12. }
    13. printf("\n");
    14. }
    15. }
    16. printf1(ARRAY, 3, 5);

    数组指针访问

    1. int ARRAY[3][5] = {
    2. 1, 2, 3, 4, 5,
    3. 2, 3, 4, 5, 6,
    4. 3, 4, 5, 6, 7};
    5. void printf2(int (*p)[5], int r, int c) // p指向第一个数组的地址
    6. {
    7. for (int i = 0; i < r; i++)
    8. {
    9. for (int j = 0; j < c; j++)
    10. {
    11. /*
    12. p+i 是每个一维数组的地址,*(p + i)解引用得到每个数组arr,arr又是每个数组的首元素 地址,进一步找到每个数组的每个元素的地址arr+j ——> *(p + i) + j
    13. */
    14. // printf("%d", *(*(p + i) + j)); // p每次加1,跳过 20个字节,即以每个数组为单位,每次加1跳过 5 个int
    15. printf("%d", p[i][j]); // 同上面一致
    16. }
    17. printf("\n");
    18. }
    19. }

     数组传参Demo

    一维数组传参

    1. void test(int arr[])//ok
    2. {}
    3. void test(int arr[10])//ok
    4. {}
    5. void test(int *arr)//ok
    6. {}
    7. void test2(int *arr[20])//ok
    8. {}
    9. void test2(int **arr)//ok
    10. {}
    11. int main()
    12. {
    13. int arr[10] = {0};
    14. int *arr2[20] = {0};
    15. test(arr);
    16. test2(arr2);
    17. }

    二维数组传参

    1. void test(int arr[3][5]) ok
    2. {}
    3. void test(int arr[][]) err 形参的二维数组,行可以省略,列不能省略
    4. {}
    5. void test(int arr[][5]) ok
    6. {}
    7. 总结:二维数组传参,函数形参的设计只能省略第一个,因为对一个二维数组,可以不知道有多少行,但是必须知道多少列
    8. void test(int *arr) err 传过来的是arr第一个数组的地址,不是一个整形的地址
    9. {}
    10. void test(int *arr[5]) err 这里表示指针数组,arr是五个指针的数组
    11. {}
    12. void test(int (*arr)[5]) ok
    13. {}
    14. void test(int **arr) ok
    15. {}
    16. int arr[3][5] = {0};
    17. test(arr);

    指针数组指针

    指向用于存放指针的数组的

    1. char *Arr[5] = {0}; // 指针数组
    2. char *(*Pc)[5] = &Arr; // Pc可以指向一个数组,该数组有5个元素,每个元素是char*类型,数组指针的类型是char* (*)[5]

    字符指针

    字符指针——>指向字符的指针

    1. char *p = "abcdef"; // 把字符串首字符a的地址,赋值给了p
    2. printf("%c\n", *p); // a
    1. char *p1 = "abcdef";
    2. char *p2 = "abcdef";
    3. char arr1[] = "abcdef";
    4. char arr2[] = "abcdef";
    5. if (p1 == p2)
    6. {
    7. printf("p1 == p2\n"); // p1 == p2
    8. }
    9. else
    10. {
    11. printf("p1 != p2\n");
    12. }
    13. if (arr1 == arr2)
    14. {
    15. printf("arr1 == arr2\n");
    16. }
    17. else
    18. {
    19. printf("arr1 != arr2\n"); // arr1 != arr2
    20. }

    函数指针

    函数指针是指向函数的指针变量。函数指针可以让你像普通变量一样操作函数,也可以将函数作为参数传递给其他函数

     函数也有地址

    1. int Add(int x, int y)
    2. {
    3. return x + y;
    4. }
    5. printf("%p\n", &Add); // 00007ff7f9ff1591
    6. printf("%p\n", Add); // 00007ff7f9ff1591

    函数指针调用时可以不用写*解引用

    1. int (*pf)(int, int) = &Add; // pf是一个函数指针,参数类型是(int, int) 返回类型为int的函数
    2. int res = (*pf)(2, 3); // 调用函数指针所指向的函数
    3. /*
    4. 函数指针调用时可以不用写*解引用
    5. int (*pf)(int, int) = Add 等同于 int *p = # p == &num
    6. int res = Add(2, 3);
    7. int res = pf(2, 3);
    8. */
    9. int resp = pf(2, 3); // 可以不用写*
    10. printf("%d\n", res); // 5

    函数作为回调传参

    1. void calc(int (*pf)(int, int))
    2. {
    3. int a = 3;
    4. int b = 5;
    5. int res = (*pf)(a, b);
    6. // int res = pf(a, b);
    7. printf("%d\n", res); // 8
    8. }
    9. /*
    10. 调用calc函数
    11. */
    12. calc(Add);

    解释: (*(void (*)())0)();

    •     void (*)() 表示一个函数指针,指向一个无返回值的函数,并且该函数没有参数。
    •     (void (*)())0 将整数 0 强制转换为一个函数指针
    •     将这个函数指针解引用,这意味着它试图调用指向地址 0 的函数。

    解释: void (*signal(int, void (*)(int)))(int);

    1. typedef unsigned int uint;
    2. typedef void (*pf_t)(int);
    3. void (*signal(int, void (*)(int)))(int);
    4. pf_t signal(int, pf_t); // 和上式相同
    • typedef unsigned int uint; 这句代码定义了一个新的数据类型 uint,它是无符号整型的别名,方便在程序中使用无符号整型时进行声明。
    • typedef void (*pf_t)(int); 这句代码定义了一个新的数据类型 pf_t,它是一个指向参数为整型、无返回值的函数指针类型。这样的定义可以方便地声明类似这种类型的函数指针变量。
    • void (*signal(int, void (*)(int)))(int); 这行代码声明了一个名为 signal 的函数,它接受两个参数:一个整型和一个指向参数为整型、无返回值的函数指针。它的返回类型是一个指向参数为整型、无返回值的函数指针。这种类型的函数通常用于信号处理。
    • pf_t signal(int, pf_t); 这行代码是函数 signal 的声明,它使用了之前定义的 pf_t 类型。它表明函数 signal 接受两个参数,一个整型和一个指向参数为整型、无返回值的函数指针,然后返回一个指向参数为整型、无返回值的函数指针。

    函数指针数组指针

    指向函数指针数组的指针

    &先看函数指针数组:函数指针也是指针,把函数和指针放在数组中,其实就是函数指针数组

    1. int (*pf)(int, int) = Add; // pf是函数指针
    2. int (*arr[4])(int, int) = {Add, Sub, Mul, Div}; // arr就是函数指针的数组
    3. for (int i = 0; i < 4; i++)
    4. {
    5. // int res = arr[i](2, 1); // 可以直接用arr[i]是因为,比如int (*pf)(int, int) = Add; 指针函数pf的类型是int (*)(int, int) 那么Add == pf
    6. int res = (*arr[i])(2, 1); // 也可以解引用,因为arr[i]指向的是函数地址 Add等函数地址
    7. printf("%d\n", res); // 3, 1, 2, 2
    8. }

    &指向函数指针数组的指针

    1. int (*pArr[])(int, int) = {0, Add, Sub, Mul, Div}; // 函数指针数组
    2. int (*(*pArr)[5])(int, int) = &pArr; // *pArr是指针,指向数组个数为5的 函数指针类型 数组的地址

  • 相关阅读:
    几款实用的照片变漫画免费软件,千万别错过
    FPGA-出租车计价器的实现
    开拓经验专栏:从十来天的晨型人体验开始
    ARM64 MMU 映射
    什么是Jmeter ?Jmeter使用的原理步骤是什么?
    (6)点云数据处理学习——RGBD图
    序列查询 CSP202112-1
    JavaScript-Obfuscator4.0.0字符串阵列化Bug及修复方法
    05 【Sass语法介绍-插值】
    ES 知识
  • 原文地址:https://blog.csdn.net/dabaooooq/article/details/134288218