• 过关斩将,擒“指针”(下)


    美丽的外貌千篇一律,有趣的灵魂万里挑一

    指针是C语言的灵魂

    应该有不少小伙伴对指针还是比较迷迷糊糊的吧

    本期 阿紫 接着带大家一起过关斩将,擒 “ 指针 ”


    目录

    1.字符指针

    2.指针数组

    3.数组指针 

    3.1数组指针的定义 

    3.2 &数组名 vs 数组名

     3.3数组指针的使用

    4.函数指针 

    5.函数指针数组 

    6. 指向函数指针数组的指针 

    7.回调函数 


    1.字符指针

    字符指针:指向字符指针,指针类型为 char*

    1. char ch = 'a';
    2. char* pc = &ch;

    一般使用方法:

    1. #include<stdio.h>
    2. int main()
    3. {
    4. char ch = 'a';
    5. char* pc = &ch;
    6. *pc = 'b';
    7. printf("%c\n", ch);
    8. return 0;
    9. }

    还有一种使用方法:

    1. #include<stdio.h>
    2. int main()
    3. {
    4. const char* pc = "abcdef";
    5. printf("%s\n", pc);
    6. return 0;
    7. }

    思考1:为什么 const char* pc = "abcdef" 前面要加 const

    答:因为字符指针 pc 指向的是常量字符串,常量是不能发生改变的,所以加 const 避免修改常量字符串。 

    思考2:const char* pc = "abcdef",是把 abcdef 字符串放入字符指针变量 pc 中吗?

    答:不是,是把字符串中首字符地址放入了 pc 中,也就是把 a 的地址放在 pc 中 。 

     面试题:

    1. #include<stdio.h>
    2. int main()
    3. {
    4. char arr[] = { "abcdef" };
    5. char brr[] = { "abcdef" };
    6. const char* crr = "abcdef";
    7. const char* drr = "abcdef";
    8. if (arr == brr)
    9. {
    10. printf("arr and brr are same\n");
    11. }
    12. else
    13. {
    14. printf("arr and brr not are same\n");
    15. }
    16. if (crr == drr)
    17. {
    18. printf("crr and drr are same\n");
    19. }
    20. else
    21. {
    22. printf("crr and drr not are same\n");
    23. }
    24. return 0;
    25. }

     运行结果:

     arrbrr 是字符数组,是把 abcdef 存储在字符数组中,它们的内存不是同一块,所以它们的地址也就不相同。而 crrdrr 是字符指针,是把 abcdef 常量字符串的首元素地址存储字符指针中,所以它们指向的是同一块内存空间。 

     2.指针数组

     指针数组:它是一个数组,数组中的每个元素都是类型指针

    int* arr[10]; //整形指针的数组

     首先 arr 先跟 [10] 结合说明它是一个有十个元素数组,每个元素都是 int* 类型的指针。

    3.数组指针 

    3.1数组指针的定义 

    数组指针:是一个指针,指向的是一个数组 

    1. int arr[6] = { 1, 2, 3, 4, 5, 6 };
    2. int(*pi)[6] = &arr;

    pi 先和 * 结合说明它是一个指针变量[6] 说明它指向了一个数组,数组中有 6 个元素,int 说明每个元素都是 int 类型。

    • 注:[] 的优先级要高于 * 号的,所以必须加上()来保证 p 先和 * 结合。 

    3.2 &数组名 vs 数组名

    int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    •  数组名 arr :表示首元素地址
    • &数组名:表示整个数组地址 

    arr &arr 它们打印出来的地址虽然一样 ,但它们代表的含义不一样。arr 表示数组首元素的地址,而 &arr 表示整个数组的地址,arr + 1跳过了一个元素,而&arr + 1跳过了整个数组。

    sizeof(arr[0])是计算 arr[0] 所占空间大小,而 sizeof(arr)是计算整个数组所占空间大小 。

    结论:&数组名sizeof(数组名) 表示整个数组,其余的数组名都表示数组首元素的地址

     3.3数组指针的使用

     既然数组指针指向的是数组,那数组指针中存放的也就是数组的地址

    1. #include<stdio.h>
    2. int main()
    3. {
    4. int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    5. int(*pi)[10] = &arr;
    6. return 0;
    7. }

    数组 arr 的地址赋值给数组指针变量 p ,但我们一般很少这样写代码 ,因为这样写代码没有任何的意义。

    一般我们将数组指针用于二维数组

    1. #include<stdio.h>
    2. void print_arr(int(*arr)[3], int row, int col)
    3. {
    4. for (int i = 0; i < row; i++)
    5. {
    6. for (int j = 0; j < col; j++)
    7. {
    8. printf("%d ", arr[i][j]);
    9. }
    10. printf("\n");
    11. }
    12. }
    13. int main()
    14. {
    15. int arr[3][3] = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };
    16. print_arr(arr, 3, 3);
    17. return 0;
    18. }

    数组名arr,表示首元素的地址  。但是二维数组的首元素是二维数组的第一行  ,所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址。

    学了指针数组和数组指针我们来一起回顾并看看下面代码的意思: 

    1. int arr[5];//整型数组
    2. int *parr1[10];//数组指针
    3. int (*parr2)[10];//指针数组
    4. int (*parr3[10])[5];//???

     int (*parr3[10])[5]:首先 parr3 跟 [10] 结合说明它是一个数组,每个元素都是指针数组类型。

    4.函数指针 

    函数指针:首先是它是一个指针,它指向的是一个函数 

     函数名&函数名:都表示函数的地址

     要是想保存函数的地址,就得用到函数指针

    p 先跟 * 结合 说明它是指针,后面的一个 () 表示它指向了一个无参的函数,前面的 void 表示返回值 void。

    《 C陷阱和缺陷 》 这本书中有这样两个代码:

    1. //代码1
    2. (*(void (*)())0)();

    将 0 强制类型转换为 void(*)() 函数指针类型,那么 0 就被当成了一个函数的地址,然后解引用调用,实际上这就是一次函数调用,调用的是 0 作为地址处的函数。

    1. //代码2
    2. void (*signal(int , void(*)(int)))(int);

    signal 先跟后面的括号结合,说明 signal 函数名,函数参数第一个参数是 int,第二个参数是函数指针(该函数指针指向的函数参数是int,返回类型是void)返回值也是一个函数指针该函数指针指向的函数参数是int,返回类型是void,所以这是一次函数声明。 

    5.函数指针数组 

    函数指针数组:首先它是一个数组,数组的每一个元素都是指针指向的是函数。 

    int(*arr[10])();

     首先 arr 先跟 [10] 结合说明它是一个数组,数组中的每一个元素都是 int(*)()类型的函数指针。

     函数指针数组的用途:转移表

    1. #include<stdio.h>
    2. void menu()
    3. {
    4. printf("*********************\n");
    5. printf("*** 1.add 2.sub ***\n");
    6. printf("*** 3.mul 4.div ***\n");
    7. printf("****** 0.exit *******\n");
    8. printf("*********************\n");
    9. }
    10. int add(int a, int b)
    11. {
    12. return a + b;
    13. }
    14. int sub(int a, int b)
    15. {
    16. return a - b;
    17. }
    18. int mul(int a, int b)
    19. {
    20. return a * b;
    21. }
    22. int div(int a, int b)
    23. {
    24. return a / b;
    25. }
    26. int main()
    27. {
    28. int a, b;
    29. int input = 1;
    30. int ret = 0;
    31. int(*p[5])(int a, int b) = { 0, add, sub, mul, div }; //转移表
    32. while (input)
    33. {
    34. menu();
    35. printf("请选择:");
    36. scanf("%d", &input);
    37. if ((input <= 4 && input >= 1))
    38. {
    39. printf("输入操作数:");
    40. scanf("%d %d", &a, &b);
    41. ret = (*p[input])(a, b);
    42. }
    43. else
    44. printf("输入有误\n");
    45. printf("ret = %d\n", ret);
    46. }
    47. return 0;
    48. }

    6. 指向函数指针数组的指针 

    指向函数指针数组的指针 :首先它是一个指针,它指向了一个数组,数组中每个元素都是函数指针。

    1. void(*p)();//函数指针
    2. void(*p1[1])();//函数指针数组
    3. void(*(*p2)[2])();//指向函数指针数组指针

    7.回调函数 

    回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个 函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数 的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进 行响应。 

    qosrt 函数使用:

    1. #include <stdio.h>
    2. //qosrt函数的使用者得实现一个比较函数
    3. int int_cmp(const void * p1, const void * p2)
    4. {
    5. return (*( int *)p1 - *(int *) p2);
    6. }
    7. int main()
    8. {
    9. int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
    10. int i = 0;
    11. qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof (int), int_cmp);//把一个函数地址传给另一个函数
    12. for (i = 0; i< sizeof(arr) / sizeof(arr[0]); i++)
    13. {
    14. printf( "%d ", arr[i]);
    15. }
    16. printf("\n");
    17. return 0;
    18. }

  • 相关阅读:
    使用百度翻译API或腾讯翻译API做一个小翻译工具
    【C语言】进阶——程序编译
    编译buildroot出错,这个怎么解决呢,感谢
    启动微服务,提示驱动程序无法通过使用安全套接字层(SSL)加密与 SQL Server 建立安全连接
    infoNCE损失和互信息的关系
    绿色校园
    [HCTF 2018]admin
    MySQL 快速入门之第一章 账号管理、建库以及四大引擎
    深度学习资源列表
    elementui表格el-table最右侧操作列展示不完全
  • 原文地址:https://blog.csdn.net/m0_66488562/article/details/125372170