• C语言之指针(中)


    目录

    前言

    一、字符指针

    1.用法(两种)

    2.例子

    二、指针数组

    三、数组指针

    1.数组指针的定义

    1.概念

    2.例子

    2.数组名

    3.使用

    1.使用的情景

    2.例子

    四、数组参数、指针参数  

    1.数组传参

    2.指针传参

    五、函数指针

    1.函数的地址

    2.函数指针

    3.例子

     4.两个特别的代码

    1.代码1

    2.代码2

    六、函数指针数组

    1.定义

    2.用途

    七、指向函数指针数组的指针

    八、回调函数

    彩蛋

    总结


    前言

    承接同系列文章C语言之指针(上),本文将进一步介绍指针的相关知识。

    一、字符指针

    char*

    1.用法(两种)

    9fb038e0ef02498893137146d122707f.png

    第一种用法是将字符型变量ch的地址放到指针pc中;

    第二种用法本质是把字符串 hello world. 首字符的地址放到了pstr中,而并非将整个字符串内容放入pstr中。

    2.例子

    1. int main()
    2. {
    3. char str1[] = "hello bit.";
    4. char str2[] = "hello bit.";
    5. const char *str3 = "hello bit.";
    6. const char *str4 = "hello bit.";
    7. if (str1 == str2)
    8. printf("str1 and str2 are same\n");
    9. else
    10. printf("str1 and str2 are not same\n");
    11. if (str3 == str4)
    12. printf("str3 and str4 are same\n");
    13. else
    14. printf("str3 and str4 are not same\n");
    15. return 0;
    16. }

    那么,这个程序最终输出的结果是什么呢?

    1434dfe5b68e440c9a92050961f538eb.png

    当两个指针指向同一个字符串的时候,他们实际上是指向同一块内存。
    但是用相同的常量字符串去初始化 ,不同的数组的时候就会开辟出不同的内存块。
    所以str1和str2不同,str3和str4相同。

    二、指针数组

    元素为指针的数组

    在指针(上)的内容中我们就介绍了指针数组。

    所以,这里做一个小测试题,回顾一下知识点。请说出以下代码分别表示什么?

    1. int* arr1[10]; //整形指针的数组
    2. char *arr2[4]; //一级字符指针的数组
    3. char **arr3[5];//二级字符指针的数组

    三、数组指针

    1.数组指针的定义

    1.概念

    我们已经知道的指针变量类型中:
    整形指针: int * pint;   即,能够指向整形数据的指针。
    浮点型指针: float * pf;  即, 能够指向浮点型数据的指针。
    那么,数组指针应该是:能够指向数组的指针

    2.例子

    1. int main()
    2. {
    3. int arr[10] = {1,2,3,4,5,6,7,8,9};
    4. int(*p)[10] = &arr;
    5. //解释:p先和*结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。
    6. //所以p是一个指针,指向一个数组,叫数组指针。
    7. //注意:①[]的优先级要高于*号的,所以必须加上()来保证p先和*结合。
    8. //②[]中的数字不能省略,省略会被编译器误认为所指向的数组是只有一个元素,即int(*p)[] = &arr[0];
    9. }

    2.数组名

    int arr[10];

    &arr表示的是整个数组的地址,arr表示的是数组首元素的地址。

    两者的区别

    675759aad33c4060b220a603b1b4e757.png

    1.数组的地址+1,跳过整个数组的大小;
    2.数组首元素的地址+1,跳过数组的一个元素大小。

    3.使用

    1.使用的情景

    一般情况下,用在一维数组时并不方便,所以我们一般多用在二维数组、多维数组。

    注意:既然数组指针指向的是数组,那数组指针中存放的应该是数组的地址。

    2.例子

    1. #include
    2. void print_arr1(int arr[3][5], int row, int col)//可以用数组接收
    3. {
    4. int i = 0;
    5. int j = 0;
    6. for (i = 0; i
    7. {
    8. for (j = 0; j
    9. {
    10. printf("%d ", arr[i][j]);
    11. }
    12. printf("\n");
    13. }
    14. }
    15. void print_arr2(int(*arr)[5], int row, int col)//也可以用指针接收
    16. {
    17. int i = 0;
    18. int j = 0;
    19. for (i = 0; i
    20. {
    21. for (j = 0; j
    22. {
    23. printf("%d ", arr[i][j]);
    24. }
    25. printf("\n");
    26. }
    27. }
    28. int main()
    29. {
    30. int arr[3][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    31. print_arr1(arr, 3, 5);
    32. //数组名arr,表示首元素的地址
    33. //二维数组的首元素是二维数组的第一行
    34. //所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址
    35. //所以可以用数组来接收,也可以用数组指针来接收
    36. print_arr2(arr, 3, 5);
    37. return 0;
    38. }

    运行结果如下:

    c5c4d0cc6fed4e83b1bc250b66f7f205.png

    四、数组参数、指针参数  

    写代码的时候难免要把【数组】或者【指针】传给函数,那函数的参数该如何设计呢?

    1.数组传参

    1.一般传数组名即可,除非要传某个元素。

    例如:

    test(arr);

    2.参数部分一般是数组或者指针。

    例如:

    1. void test1(int arr[ ]);//[]中的数字可写可不写
    2. {}
    3. void test2(int *parr);
    4. {}
    3.以上是一维数组例子,二维数组与一维数组差不多,但是 二维数组传参,函数形参的设计 只能省略第一个[ ]的数字
    因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。 这样才方便运算。

    2.指针传参

    指针传参,一般用指针接收。

    一级指针传参,用一级指针接收;

    二级指针传参,用二级指针接收。

    五、函数指针

    指向函数地址的指针

    1.函数的地址

    对于函数来说,&函数名和函数名都是表示函数的地址。

    2.函数指针

    1. int ADD(int x,int y)
    2. {}
    3. int (*pf)(int,int) = ADD;//pf是指向参数为(int,int)且返回值是int型的函数的指针
    4. int (*pf)(int,int) = &ADD;//形参可以不写,但是要写明形参的类型

    3.例子

    1. int ADD(int x, int y)
    2. {
    3. return x + y;
    4. }
    5. void test(int (*pf)(int,int))
    6. {
    7. int a = 0;
    8. int b = 0;
    9. int ret = (*pf)(a, b);//可以这样使用,由于pf里面存的就是函数的地址,所以和ADD是一样的,也可以用下面的方式使用
    10. //int ret = pf(a, b);//*就是一个摆设,方便理解是对指针pf解引用才调用函数ADD的,所以*可以不写或者写很多个。
    11. }
    12. #include
    13. int main()
    14. {
    15. test(ADD);//将函数ADD()的地址传参给函数test()
    16. return 0;
    17. }

     4.两个特别的代码

    这两个代码都是我在《C陷阱与缺陷》书中看到的例子,因为它们与函数指针有关系,并且十分特别所以也向大家介绍一下。

    1.代码1

    1. //代码1
    2. int main()
    3. {
    4. ( * ( void ( * ) ( ) ) 0 ) ( );
    5. }

     说明:以上是一次函数调用,调用的是0作为地址的函数

    分析过程分为两步:

    (1)把0强制类型转换为无参数,返回值是void*类型的函数的地址

    (2)调用0地址处的函数

    2.代码2

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

     说明:以上代码是一次函数声明

    分析:

    (1)首先,signal是这个函数的函数名

    (2)函数的参数:第一个参数类型是int,第二个参数类型是函数指针(该函数指针指向的函数参数是int,返回值是void)

    (3)可以观察出里外两个函数指针的类型是相同的。

    这里可以用typedef重定义一下这个函数类型的类型名,就可以对上面的代码进行相应的简化了。例如:

    1. typedef void(*pf_t) (int);//此时,这个函数指针类型的名字的重定义为pf_t了
    2. //相应的上述代码就可以简化为:
    3. pf_t signal(int, pf_t);

    六、函数指针数组

    1.定义

    我们都知道,数组是一个存放相同类型数据的存储空间,那么存放函数指针的数组就是 函数指针数组
    int (*parr1[10])();

    解释:数组名是parr1,该数组中存放有10个int(* )()类型的函数指针。

    2.用途

    转移表

    七、指向函数指针数组的指针

    本质上还是个指针,指向存放函数指针的数组的首元素地址。

    定义:

    1. void test(const char* str)
    2. {
    3. printf("%s\n", str);
    4. }
    5. int main()
    6. {
    7. //函数指针pfun
    8. void(*pf)(const char*) = test;
    9. //函数指针的数组pfArr
    10. void(*pfArr[5])(const char* str);
    11. pfArr[0] = test;
    12. //指向函数指针数组pfArr的指针ppfunArr
    13. void(*(*ppfArr)[5])(const char*) = &pfArr;
    14. return 0;
    15. }

    八、回调函数

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

    彩蛋

    大家看这样一个代码,请思考一下最后一个表示的是什么呢?

    1. #include
    2. int main()
    3. {
    4. int arr[5];//整型数组
    5. int *p1[10];//整型指针数组
    6. int(*p2)[10];//整型数组指针
    7. int*(*p3[10])[5];//?
    8. return 0;
    9. }

    没错,它表示的是数组指针数组(存放数组指针的数组)

    bcac7b40aec3413c8bf4955d8a83e076.png


    总结

    以上就是今天要讲的内容,本文介绍了C语言中指针的相关知识,主要包括有字符指针指针数组数组指针数组参数与指针参数函数指针函数指针数组指向函数指针数组的指针以及回调函数的知识。但由于篇幅原因,在这一篇文章中仍不能将所有的知识全部总结完,因此后续还是会不断补充关于C语言中的指针的内容,希望大家继续支持。

    本文的作者也只是一个正在学习C语言等编程知识的萌新,若这篇文章中有哪些不正确的内容,请在评论区向作者指出(也可以私信作者),欢迎大佬们指点,也欢迎其他正在学习C语言的萌新和作者进行交流。

    最后,如果本篇文章对你有所启发的话,也希望可以支持支持作者,后续作者也会定期更新学习记录。谢谢大家!

    2a79e5b7935a4f518a3156d5046aad96.gif

  • 相关阅读:
    解决cocos2d-x-4.0 Android Demo构建遇到的问题
    Matlab:Unicode 和 ASCII 值
    bat & cmd 命令整理(待更新)
    Unity 3D模型展示框架篇之ILRuntime整合与应用
    Java筑基31-IO流01-文件&流分类
    Jackson 中的 @JsonProperty 和 @JsonAlias 的区别
    有关使用HttpServletRequest的Cookie的设置和获取
    Java面试题(每天10题)-------连载(31)
    RTOS系列(13):提升RTOS实时性的算法技巧分析
    Java核心技术卷Ⅰ-第三章Java的基本程序设计结构
  • 原文地址:https://blog.csdn.net/xjjxjy_2021/article/details/127736443