• 【进阶C语言】进阶指针--学会所有指针类型


     本节内容大致目录:

    1.字符指针

    2.指针数组(数组)

    3.数组指针 (指针)--比较重要

    4.函数指针--比较重要

    5.函数指针数组--用的较少

    6.指向函数指针数组的指针--只需要了解就可以

    需要掌握每一种类型的符号和用处。


    一、字符指针

    前言:字符指针是一种指针,是众多指针类型中的一种。

    1.字符指针的三种形式

    (1)指向单个字符

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

    (2)指向一个字符数组

    1. #include
    2. int main()
    3. {
    4. char arr[] = "abcdef";
    5. char* pc = arr;
    6. printf("%s\n",pc);
    7. return 0;
    8. }

    (3)直接指向一个字符串

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

    这里是把整个字符串放到pc的指针变量里面了吗?其实并不是,字符串跟数组一样,首元素就是首地址。只是把字符串第一个字符的地址存入指针变量中,通过首地址就可以找到整个字符串。

     

    2.指向字符串和指向字符数组指针的区别 

    我们看下面的代码:

    1. #include
    2. int main()
    3. {
    4.   char str1[] = "abcdef.";//指向字符数组
    5.   char str2[] = "abcdef.";//的字符指针
    6.   const char *str3 = "abcdef.";//直接指向字符串
    7.   const char *str4 = "abcdef.";//的字符指针
    8.   if(str1 ==str2)
    9. printf("str1 and str2 are same\n");
    10.   else
    11. printf("str1 and str2 are not same\n");
    12.   
    13.   if(str3 ==str4)
    14. printf("str3 and str4 are same\n");
    15.   else
    16. printf("str3 and str4 are not same\n");
    17.   
    18.   return 0;
    19. }

    结果:

    第一组:

    比较的是地址,虽然数组str1和数组str2中的内容一模一样,但是他们的地址不一样,也就是所,地址所指向的内存不是同一块

     第二组:

    1.C/C++会把常量字符串存储到单独的一个内存区域,当
    几个指针。指向同一个字符串的时候,他们实际会指向同一块内存。

    2.str3和str4比的是地址,地址是存放别人的地址,也就是字符串的地址

     

    二、指针数组

    指针数组是数组,里面存放的数组都是指针类型---前面已经提到过

    格式:

    1. int* arr1[5];
    2. char* arr2[6];

    指针数组的应用:模拟二维数组、存放字符串

    1.指针数组传参

    当指针数组传参时,形参写成二级指针的形式。

    三、数组指针

    1.数组指针的定义

    (1)数组指针,是指向数组的一种指针。

    (2)数组指针的表示形式

    1. #include
    2. int main()
    3. {
    4. int arr1[10] = { 0 };
    5. int(*p1)[10] = &arr1;
    6. char* arr2[5] = { 0 };
    7. char* (*p2)[5] = &arr2;
    8. return 0;
    9. }

    数组名不就是首地址吗?为什么还需要取地址?因为&arr和arr的意义不同!我们通过下面的代码验证。
    数组指针初始化必须指定数组大小
    1. #include
    2. int main()
    3. {
    4. int arr[10] = { 0 };
    5. printf("arr = %p\n", arr);
    6. printf("arr+1 = %p\n", arr + 1);
    7. printf("&arr= %p\n", &arr);
    8. printf("&arr+1= %p\n", &arr + 1);
    9. return 0;
    10. }

    结果展示:

    我们知道,指针的类型决定了指针+1或者-1的时候跳过多少字节。

    比如:整形指针+1会跳过4字节,而数组指针的类型是数组,那么+1肯定是需要跳过一个数组(上述一个数组为40字节)。

    因为&arr的含义是取出整个数组的地址,而arr只是数组首元素的地址,+1只会跳过一个数组类型。

     2.数组指针的使用

    前言:常用于二维数组传参

    (1)了解二维数组的特性

    1.二维数组==一维数组的数组

    2.二维数组的数组名==二维数组中第一行的地址

    (2)二维数组的传参问题--引出数组指针

    1)形参是二维数组的形式

    1. #include
    2. void test(int arr[3][5],int r,int c)
    3. {
    4. int i = 0;
    5. for (i=0;i
    6. {
    7. int j = 0;
    8. for (j=0;j
    9. {
    10. printf("%d ",arr[i][j]);
    11. }
    12. printf("\n");
    13. }
    14. }
    15. int main()
    16. {
    17. int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
    18. test(arr,3,5);
    19. return 0;
    20. }

    这是二维数组传参,形参部分也是二维数组的形式(行号可以省略,但是列不能省)

    2)形参是数组指针的形式

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

    1.二维数组的数组名是第一行元素的地址

    2.第一行是一个一维数组,传的是一维数组的地址

    3.是地址,所以是指针;是一维数组,要求类型是数组类型;结合起来就是数组指针(的类型)。(类比:整形变量传地址,形参需要用整形指针的类型来接收)

    数组指针的一个作用:作为二维数组传参的形式参数


    四、函数指针

    前言:函数指针是指向函数的指针,也属于指针类型的一种

    1.函数指针的定义

    (1)了解函数名

    &函数名与函数名的区别

    1. #include
    2. void test()
    3. {
    4. printf("hhhh\n");
    5. }
    6. int main()
    7. {
    8. test();
    9. printf("%p\n",test);
    10. printf("%p\n",&test);
    11. return 0;

    运行结果:

    1.函数名就是函数的地址

    2.&函数名与函数名没有区别,都是函数的地址

    (2)函数指针变量

    函数指针变量就是存放函数的地址,跟正常的指针变量一样

    函数指针的格式:

    1. #include
    2. void test()
    3. {
    4. printf("hhhh\n");
    5. }
    6. int main()
    7. {
    8. test();
    9. void (*p)() = &test;函数指针
    10. return 0;
    11. }

    图解:

    1.函数指针的格式与数组指针的格式及其相似

    2.去掉变量的名字,剩下的就是变量的类型

    下面来认识函数指针的初步使用:

    (3)通过函数指针调用函数

    第一种:

    1. #include
    2. int Add(int x, int y)
    3. {
    4. return x + y;
    5. }
    6. int main()
    7. {
    8. int (*p)(int,int) = &Add;
    9. int ret = 0;
    10. ret = (*p)(3, 5);//第一种
    11. printf("%d\n",ret);
    12. return 0;
    13. }

     若通过*解引用操作,必须有括号(*号可以有很多个或者没有,对结果没有影响)

    第二种:

    1. #include
    2. int Add(int x, int y)
    3. {
    4. return x + y;
    5. }
    6. int main()
    7. {
    8. int (*p)(int,int) = &Add;
    9. int ret = 0;
    10. ret = p(3, 5);//第二种
    11. printf("%d\n",ret);
    12. return 0;
    13. }

    通过函数的地址(与Add(3,5)直接调用类似)调用函数

    五、函数指针数组

    1.函数指针数组定义

    1.首先,是一个数组

    2.是一个存放函数指针的数组,也就是存放函数地址的数组。

    (1)代码定义:

    1. #include
    2. int Add(int x, int y)
    3. {
    4. return x + y;
    5. }
    6. int Sub(int x,int y)
    7. {
    8. return x - y;
    9. }
    10. int main()
    11. {
    12. int (*p1)(int, int) = &Add;
    13. int (*p2)(int,int) = ⋐
    14. //p1、p2是两个函数指针
    15. int (*p[4])(int, int) = {Add,Sub};
    16. //p就是函数指针数组
    17. return 0;
    18. }

    (2)图解:

    2.函数指针数组的用途(转移表)

    应用:做一个计数器

    先看没有使用函数指针数组前的代码:

    1. #include
    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. //加法
    11. int Add(int x, int y)
    12. {
    13. return x + y;
    14. }
    15. //减法
    16. int Sub(int x, int y)
    17. {
    18. return x - y;
    19. }
    20. //乘法
    21. int Mul(int x, int y)
    22. {
    23. return x * y;
    24. }
    25. //除法
    26. int Div(int x, int y)
    27. {
    28. return x / y;
    29. }
    30. int main()
    31. {
    32. int input = 0;
    33. int x = 0;
    34. int y = 0;
    35. int ret = 0;
    36. do
    37. {
    38. menu();
    39. printf("请选择:>");
    40. scanf("%d", &input);
    41. switch (input)
    42. {
    43. case 1:
    44. printf("请输入2个操作数:");
    45. scanf("%d %d", &x, &y);
    46. ret = Add(x, y);
    47. printf("ret = %d\n", ret);
    48. break;
    49. case 2:
    50. printf("请输入2个操作数:");
    51. scanf("%d %d", &x, &y);
    52. ret = Sub(x, y);
    53. printf("ret = %d\n", ret);
    54. break;
    55. case 3:
    56. printf("请输入2个操作数:");
    57. scanf("%d %d", &x, &y);
    58. ret = Mul(x, y);
    59. printf("ret = %d\n", ret);
    60. break;
    61. case 4:
    62. printf("请输入2个操作数:");
    63. scanf("%d %d", &x, &y);
    64. ret = Div(x, y);
    65. printf("ret = %d\n", ret);
    66. break;
    67. case 0:
    68. printf("退出计算器\n");
    69. break;
    70. default:
    71. printf("选择错误, 重新选择\n");
    72. break;
    73. }
    74. } while (input);
    75. return 0;
    76. }

    1.该计数器可以实现的功能:加减乘除

    2.我们可以发现上面的代码很冗长,很多啰嗦的代码

    观察发现:每个功能函数的返回值类型、函数参数类型及个数都一样,属于同类型函数,这就可以联想到数组。存放函数地址的数组,自然而然就是函数指针数组。

    改进后:

    1. #include
    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. //加法
    11. int Add(int x, int y)
    12. {
    13. return x + y;
    14. }
    15. //减法
    16. int Sub(int x, int y)
    17. {
    18. return x - y;
    19. }
    20. //乘法
    21. int Mul(int x, int y)
    22. {
    23. return x * y;
    24. }
    25. //除法
    26. int Div(int x, int y)
    27. {
    28. return x / y;
    29. }
    30. int main()
    31. {
    32. int input = 0;
    33. int x = 0;
    34. int y = 0;
    35. int ret = 0;
    36. do
    37. {
    38. menu();
    39. printf("请选择:>");
    40. scanf("%d", &input);
    41. //函数指针数组 - 转移表
    42. int (*pfArr[])(int, int) = {NULL, Add, Sub, Mul, Div};
    43. // 为了与菜单选项对应上 0 1 2 3 4
    44. if (0 == input)
    45. {
    46. printf("退出计算器\n");
    47. }
    48. else if (input >= 1 && input <= 4)
    49. {
    50. printf("请输入2个操作数:");
    51. scanf("%d %d", &x, &y);
    52. ret = pfArr[input](x, y);//函数指针数组的使用
    53. printf("ret = %d\n", ret);
    54. }
    55. else
    56. {
    57. printf("选择错误,重新选择!\n");
    58. }
    59. } while (input);
    60. return 0;
    61. }

    1.经过对比,明显改进后的更加简洁

    2.函数指针数组的使用:直接通过数组下标的引用,再传参即可

    六、指向函数指针数组的指针(了解)

    1.给出定义(类比)

    (1)指向整形指针数组的指针

    1. #include
    2. int main()
    3. {
    4. int a = 1;
    5. int b = 2;
    6. int c = 3;
    7. //整形指针数组
    8. int* arr[3] = {&a,&b,&c};
    9. //指向整形指针数组的指针
    10. int* (*p)[3] = &arr;
    11. return 0;
    12. }

    p为指向整形指针数组的指针变量,*说明p是指针。把*p去掉,剩下就是该指针变量指向的类型。

    (2)指向函数指针数组的指针


  • 相关阅读:
    JSP 四大域对象
    MATLAB算法实战应用案例精讲-【优化算法】冠状病毒优化算法(COVIDOA)(附MATLAB代码实现)
    大厂面试题:【SpringBoot篇面试题:1-5题】
    springboot集成uid-generator生成分布式id
    CSDN常用复杂公式模板记录
    Python环境和PyCharm搭建教程
    静态代理和动态代理
    2核2G3M带宽云服务器99元(续费同价),阿里云老用户可买!
    BGP选路的十一条原则(第八课)
    【JavaSE】抽象类和接口
  • 原文地址:https://blog.csdn.net/2301_77053417/article/details/133544556