当前位置:   article > 正文

嵌入式学习之路 16(C语言基础学习——指针操作二维数组、指向函数的指针、指针的指针)

嵌入式学习之路 16(C语言基础学习——指针操作二维数组、指向函数的指针、指针的指针)

一、指针操作二维整型数组

1、二维数组的本质

        在 C 语言中,二维数组本质上是由多个一维数组组成的。例如,int a[2][3]可以看作是包含两个长度为 3 的一维整数数组。

2、指针与二维数组

        对于二维数组int a[2][3],&a[0]的类型是int (*)[3],因为 C 语言中不直接支持int[3] *这种类型,所以需要使用int (*)[3]来表示指向包含 3 个整数的一维数组的指针。
定义指针int (*p)[3] = a;p指向了二维数组a的首地址,其基类型是int[3]。

3、通过指针访问二维数组元素

*p相当于a[0],即指向二维数组的第一行(内部的一维数组)。
(*p)[0]表示第一行的第一个元素,等同于a[0][0]。
*(*p + 0)也表示第一行的第一个元素。
*(*(p + 1) + 1)相当于a[1][1],即指向二维数组的第二行的第二个元素。
一般地,*(*(p + i) + j)就相当于a[i][j],可以通过这种方式遍历二维数组的元素。

例如,如果要通过指针遍历二维数组a的所有元素,可以使用以下代码:

  1. for (int i = 0; i < 2; i++)
  2. {
  3. for (int j = 0; j < 3; j++)
  4. {
  5. printf("%d ", *(*(p + i) + j));
  6. }
  7. printf("\n");
  8. }

4、将二维整型数组作为参数传递给函数

以数组形式传递:第二维的大小必须指定

  1. void function(int arr[][3], int rows)
  2. {
  3. // 函数体
  4. }
  5. int main()
  6. {
  7. int a[2][3] = {{1, 2, 3}, {4, 5, 6}};
  8. function(a, 2);
  9. return 0;
  10. }

以指针形式传递:这种方式与以数组形式传递本质上是相同的

  1. void function(int (*arr)[3], int rows)
  2. {
  3. // 函数体
  4. }
  5. int main()
  6. {
  7. int a[2][3] = {{1, 2, 3}, {4, 5, 6}};
  8. function(a, 2);
  9. return 0;
  10. }

二、指针操作二维字符型数组

char s[][10] = {"hello","world","china"};

char (*p)[10] = s; 

定义了一个指针 p ,它指向一个包含 10 个字符的一维字符数组,这里 p 指向了二维数组 s 的起始位置。

*(*(p + i) + j) 可以用于访问二维字符型数组 s 中特定位置的字符。

  1. void putStr(char (*p)[10], int row)
  2. {
  3. int i=0;
  4. for ( i=0; i<3; i++ )
  5. {
  6. printf("%s \n",*(p+i));
  7. }
  8. }
  9. int main()
  10. {
  11. char s[][10] = {"hello","world","China"};
  12. char (*p)[10] = s;
  13. putStr(s,3);
  14. return 0;
  15. }

三、函数指针

1、函数指针的概念

        函数指针是一种特殊的指针,它指向函数的入口地址。函数指针的类型由函数的返回值类型和参数类型共同决定。

2、函数指针的定义和初始化

在代码中,例如:

int (*p)(int);

定义了一个函数指针 p ,它指向一个返回值为 int 且有一个 int 类型参数的函数。

可以通过将函数名赋给函数指针来进行初始化,如 p = func1; 。

例:

  1. // 定义一个指向返回值为 int,带有两个 int 型参数的函数的指针
  2. int (*ptr)(int, int);

可以将函数的地址赋给函数指针,例如,如果有函数 int add(int a, int b) ,则可以这样赋值: 

ptr = add;
int result = ptr(2, 3);  // 等价于 int result = add(2, 3);

3、回调函数

        回调函数是一种通过函数指针实现的机制。在某些情况下,一个函数(通常称为主调函数)可能在执行过程中需要调用另一个函数(回调函数)来完成特定的任务。主调函数事先不知道回调函数的具体实现,但通过传递函数指针来在需要的时候调用它。

  1. #include <stdio.h>
  2. // 定义一个函数,返回输入参数的值
  3. int func1(int n)
  4. {
  5. return n;
  6. }
  7. // 定义一个函数,返回输入参数的平方
  8. int func2(int n)
  9. {
  10. return n*n;
  11. }
  12. // 定义一个函数,返回输入参数的立方
  13. int func3(int n)
  14. {
  15. return n*n*n;
  16. }
  17. // 选择排序函数,根据传入的函数指针确定排序规则
  18. void choieSortN(int *a, int len, int (*p)(int))
  19. {
  20. int i = 0;
  21. int j = 0;
  22. for (i = 0; i < len - 1; i++) // 外层循环控制排序轮数
  23. {
  24. for (j = i + 1; j < len; j++) // 内层循环每一轮比较次数
  25. {
  26. if (p(a[i]) > p(a[j])) // 根据传入的函数计算值进行比较
  27. {
  28. int temp = a[i]; // 交换元素
  29. a[i] = a[j];
  30. a[j] = temp;
  31. }
  32. }
  33. }
  34. }
  35. // 打印数组函数
  36. void printfArray(int *a, int len)
  37. {
  38. int i = 0;
  39. for (i = 0; i < len; i++) // 遍历数组并打印每个元素
  40. {
  41. printf("%d ", a[i]);
  42. }
  43. printf("\n"); // 换行
  44. }
  45. // 定义加法函数
  46. int ADD(int a, int b)
  47. {
  48. return a + b;
  49. }
  50. // 定义减法函数
  51. int SUB(int a, int b)
  52. {
  53. return a - b;
  54. }
  55. // 定义乘法函数
  56. int MUL(int a, int b)
  57. {
  58. return a * b;
  59. }
  60. // 定义除法函数
  61. int DIV(int a, int b)
  62. {
  63. return a / b;
  64. }
  65. // 处理数据函数,根据传入的函数指针进行计算并打印结果
  66. void processDate(int a, int b, int (*p)(int, int))
  67. {
  68. printf("ret = %d\n", p(a, b)); // 打印计算结果
  69. }
  70. int main()
  71. {
  72. int a[] = {6, -7, 3, -9, 5, 1, 8, -4, 2}; // 定义并初始化整数数组
  73. int len = sizeof(a) / sizeof(a[0]); // 计算数组长度
  74. choieSortN(a, len, func1); // 以 func1 规则排序
  75. printfArray(a, len); // 打印排序后的数组
  76. choieSortN(a, len, func2); // 以 func2 规则排序
  77. printfArray(a, len); // 打印排序后的数组
  78. choieSortN(a, len, func3); // 以 func3 规则排序
  79. printfArray(a, len); // 打印排序后的数组
  80. processDate(3, 2, ADD); // 计算 3 和 2 的加法
  81. processDate(3, 2, SUB); // 计算 3 和 2 的减法
  82. processDate(3, 2, MUL); // 计算 3 和 2 的乘法
  83. processDate(3, 2, DIV); // 计算 3 和 2 的除法
  84. return 0;
  85. }

4、回调函数的实现

        choieSortN 函数是一个排序函数,它接受一个整数数组 a 、数组长度 len 和一个函数指针 p 作为参数。在函数内部,通过比较 p(a[i]) 和 p(a[j]) 的结果来进行排序。这就是回调函数的应用,通过传递不同的函数指针(如 func1 、 func2 、 func3 ),可以实现基于不同规则的排序。
        processDate 函数接受两个整数 a 、 b 和一个函数指针 p ,通过 p(a, b) 来调用传入的函数进行计算,并打印结果。

5、函数指针的用途

增加代码的灵活性和可扩展性,使得主调函数可以根据不同的需求调用不同的回调函数。
实现事件驱动编程,当特定事件发生时,调用相应的处理函数。
封装和隐藏函数的实现细节,只暴露函数指针供外部使用。

四、指针的指针

指针的指针(也称为二级指针)是指向指针的指针变量。

  1. #include <stdio.h>
  2. int main()
  3. {
  4. int num = 10; // 定义一个整数变量
  5. int *ptr = &num; // 定义一个指针指向 num
  6. int **ptrPtr = &ptr; // 定义一个二级指针指向 ptr
  7. // 通过二级指针访问和修改 num 的值
  8. printf("初始值:%d\n", **ptrPtr);
  9. **ptrPtr = 20; // 修改 num 的值
  10. printf("修改后的值:%d\n", **ptrPtr);
  11. return 0;
  12. }

在上述代码中:

int num = 10; 定义了一个整数 num ,值为 10 。
int *ptr = &num; 定义了一个指针 ptr ,并使其指向 num 。
int **ptrPtr = &ptr; 定义了一个二级指针 ptrPtr ,并使其指向指针 ptr 。

通过二级指针,可以间接访问和修改其所指向的指针所指向的变量的值。

指针的指针常用于一些复杂的数据结构,如链表、二叉树等,或者在函数中需要修改指针的值时使用。

五、指针的指针数组

指针数组是一个数组,其中的每个元素都是一个指针。

  1. #include <stdio.h>
  2. int main()
  3. {
  4. // 定义三个整数变量
  5. int num1 = 10, num2 = 20, num3 = 30;
  6. // 定义一个指针数组,每个元素都是指向 int 类型的指针
  7. int *ptrArray[] = {&num1, &num2, &num3};
  8. // 通过指针数组访问和打印变量的值
  9. for (int i = 0; i < 3; i++)
  10. {
  11. printf("%d ", *(ptrArray[i]));
  12. }
  13. printf("\n");
  14. return 0;
  15. }

        代码中定义了一个指针数组 ptrArray ——int *ptrArray[] = {&num1, &num2, &num3}; 并将三个整数变量的地址分别存储在数组的元素中。在 for 循环中,通过 *(ptrArray[i]) 来获取每个指针所指向的整数的值并进行打印。

六、指针数组与数组指针

指针数组是一个数组,其中的每个元素都是一个指针。

int* arr1[5];  // 定义了一个包含 5 个指向 int 类型的指针的数组

arr1 是一个指针数组,它的每个元素都可以用来指向一个 int 类型的变量。 

数组指针是指向一个数组的指针。

int (*arr2)[5];  // 定义了一个指向包含 5 个 int 类型元素的数组的指针

要区分指针数组和数组指针,可以通过以下方式记忆:

指针数组:先看“数组”,意味着这是个数组,再看“指针”,说明数组中的元素是指针。
数组指针:先看“指针”,表示这是个指针,再看“数组”,表明指针指向的是一个数组。

  1. #include <stdio.h>
  2. int main()
  3. {
  4. int num1 = 10, num2 = 20, num3 = 30, num4 = 40, num5 = 50;
  5. // 指针数组的使用
  6. int* ptrArray[5] = {&num1, &num2, &num3, &num4, &num5};
  7. for (int i = 0; i < 5; i++)
  8. {
  9. printf("指针数组中第 %d 个元素指向的值: %d\n", i, *ptrArray[i]);
  10. }
  11. // 数组指针的使用
  12. int arr[3][5] =
  13. {
  14. {1, 2, 3, 4, 5},
  15. {6, 7, 8, 9, 10},
  16. {11, 12, 13, 14, 15}
  17. };
  18. int (*ptr)[5] = arr;
  19. for (int i = 0; i < 3; i++)
  20. {
  21. for (int j = 0; j < 5; j++)
  22. {
  23. printf("数组指针指向的数组中第 %d 行第 %d 列的值: %d\n", i, j, (*ptr)[j]);
  24. }
  25. ptr++; // 移动指针指向下一行
  26. }
  27. return 0;
  28. }

七、main函数的参数

  1. #include <stdio.h>
  2. // 主函数,接收命令行参数
  3. int main(int argc, const char *argv[])
  4. {
  5. // 打印命令行参数的数量
  6. printf("argc = %d\n", argc);
  7. int i = 0;
  8. // 遍历所有的命令行参数
  9. for (i = 0; i < argc; i++)
  10. {
  11. // 打印每个参数的索引和参数值
  12. printf("argv[%d] = %s\n", i, argv[i]);
  13. }
  14. return 0;
  15. }

        这段代码主要用于处理命令行参数。 在 `main` 函数中,`argc` 表示命令行参数的数量,包括程序名本身。 `argv` 是一个字符指针数组,其中每个元素指向一个命令行参数的字符串。 例如,在命令行中运行 `./a.out arg1 arg2 arg3` 时: - `argc` 的值为 `4`,因为有程序名 `./a.out` 以及三个参数 `arg1`、`arg2`、`arg3`。

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/Guff_9hys/article/detail/950696
推荐阅读
相关标签
  

闽ICP备14008679号