• C语言 指针进阶 贰


    1.字符指针

    const关键字:
    1.修饰局部变量,表示变量的值不能被改变了,用const修饰变量时,一定要给变量初始化,否则之后不能赋值
    const 用于修饰常量字符串

    2.常量指针与指针常量
    指针常量指向的地址不能改变,但是地址中存储的值可以改变

    3.修饰函数的参数

    1. int main()
    2. {
    3. char ch = 'a';
    4. char* pc = &ch;
    5. const char* ps = "abcdef";//将字符串首字符'a’的位置存放在指针中 ps指向字符串
    6. //ps存放的是字符串首字符'a'的地址
    7. //const可以修饰指针变量 使得指针存放的值不能被修改 用const ps维护指针更加安全
    8. return 0;
    9. }

    字符指针——指向字符的指针——存放字符的地址
    char ch = 'a';
    char* pc = &ch;

    整形指针——指向整形的指针——存放整形的地址
    int num = 10;
    int* pa = #

    数组指针——本质是指针——存放数组的地址
    int arr[10] = { 0 };

    *********int (*p)[10] = &arr;*********//拿到的是数组的地址

    数组名的理解:
    数组名绝大部分情况下,数组名是数组首元素的地址
    1.sizeof (数组名),数组名表示整个数组,计算的是整数数组的大小
    2.&数组名,数组名也表示整个数组,取出的是整个数组的地址

    除此之外,所有的数组名都是数组首元素的地址!

    二维数组传参的时候,传递的是数组首元素的地址,也就是第一行的地址,就可以使用数组指针来接收

    3.指针数组
    指针数组——本质是数组——是存放指针的数组
    char* arr[5];//存放字符指针的数组
    int* arr2[6];//存放整形指针的数组

    使用数组指针来模拟一个二维数组:

    1. //使用数组指针来模拟一个二维数组:
    2. int main()
    3. {
    4. int arr1[5] = { 1,2,3,4,5 };
    5. int arr2[5] = { 2,3,4,5,6 };
    6. int arr3[5] = { 3,4,5,6,7 };
    7. int* arr[3] = { arr1,arr2,arr3};
    8. for (int i = 0; i < 3; i++)
    9. {
    10. for (int j = 0; j < 5; j++)
    11. {
    12. printf("%d ", arr[i][j]);
    13. }
    14. printf("\n");
    15. }
    16. return 0;
    17. }

    标答:

    1. int main()
    2. {
    3. int arr1[] = { 1,2,3,4,5 };
    4. int arr2[] = { 2,3,4,5,6 };
    5. int arr3[] = { 3,4,5,6,7 };
    6. //int* int* int*
    7. int* arr[] = { arr1,arr2,arr3 };
    8. for (int i = 0; i < 3; i++)
    9. {
    10. for (int j = 0; j < 5; j++)
    11. {
    12. printf("%d ", arr[i][j]);//通过访问数组下标来访问每一个元素
    13. }
    14. printf("\n");
    15. }
    16. return 0;
    17. }

    4.数组传参和指针传参
    数组传参的时候:
    1.传递的是数组首元素的地址
    1>一维数组传参,传递的是第一个元素的地址
    2>二维数组传参,传递的是第一行的地址
    2.数组传参的时候,形参可以写成数组名,也可以写成指针

    举例:
     形参是数组
    test(int a[10]){}
    test(int a[]){}

    形参是指针
    test(int *p){}

    二维数组传参

    int arr[10];
    test(arr);
    形参写成数组:test(int arr[3][5]){} 
                 test(int arr[][5]{}  行数可省略,列数不可省略
    形参写成指针:test(int (*p)[5]){}//指针默认指向数组第一行

    函数指针:函数指针:指向函数的指针 存放的是函数的地址 类比字符指针 整形指针 数组指针

    1. int Add(int x, int y)
    2. {
    3. return x + y;
    4. }

    &函数名和函数名 都是函数的地址

    1. int main()
    2. {
    3. printf("%p\n", &Add);//打印的是函数的地址 00007FF7CA5611CC
    4. printf("%p\n", Add);//打印的是函数的地址 00007FF6DEDB11CC
    5. int (*pf)(int, int) = &Add;
    6. // 返回类型 参数类型 函数名或取地址函数名
    7. printf("%p\n", pf);//pf是函数指针变量 打印的是函数的地址 00007FF6DEDB11CC
    8. int arr[10];
    9. int(*pa)[10] = &arr;//数组指针
    10. printf("%p\n", pa);//pa是数组指针变量 打印的是数组的地址 000000C3848FFAB8
    11. int ret = pf(3, 5);//可以通过函数指针直接调用函数 (*pf)(3, 5); *可有可无
    12. int ret2 = Add(3, 5);//也可以通过函数名直接调用函数
    13. printf("%d\n", ret);
    14. printf("%d\n", ret2);
    15. return 0;
    16. }

    6.函数指针数组
    函数指针数组:
    char* arr[5] ;字符指针数组 数组 存放的是字符指针
    int* arr2[6];  整形指针数组 数组 存放的是整形指针
                          函数指针数组 数组 存放的是函数指针

    1. int Add(int x, int y)
    2. {
    3. return x + y;
    4. }
    5. int Sub(int x, int y)
    6. {
    7. return x - y;
    8. }
    9. int main()
    10. {
    11. int (*pf1)(int, int) = &Add;
    12. int (*pf2)(int,int) = ⋐
    13. //数组中可以存放类型相同的多个元素
    14. int (* pfArr[4])(int, int) = { &Add, &Sub };//pfArr是函数指针数组 是存放函数指针的数组
    15. //数组有四个元素 每个元素都是存放一个地址的指针
    16. return 0;
    17. }

    函数指针数组的应用 函数指针数组的用途

    模拟计算器

    1. void menu()
    2. {
    3. printf("*****************************************\n");
    4. printf("************ 1.add 2.sub ***********\n");
    5. printf("************ 3.mul 4.div ***********\n");
    6. printf("************ 0.exit ***********\n");
    7. printf("*****************************************\n");
    8. }
    9. int add(int x, int y)
    10. {
    11. return x + y;
    12. }
    13. int sub(int x, int y)
    14. {
    15. return x - y;
    16. }
    17. int mul(int x, int y)
    18. {
    19. return x * y;
    20. }
    21. int div(int x, int y)
    22. {
    23. return x / y;
    24. }
    25. int main()
    26. {
    27. int input = 0;
    28. int x = 0;
    29. int y = 0;
    30. int ret = 0;
    31. do
    32. {
    33. menu();
    34. printf("请选择:>\n");
    35. scanf("%d", &input);
    36. //创建一个函数指针数组
    37. //函数指针数组 转移表
    38. int (*pfarr[])(int, int) = { null,add,sub,mul,div };
    39. // 0 1 2 3 4
    40. if (0 == input)
    41. {
    42. printf("退出计算器\n");
    43. }
    44. else if (input >= 1 && input <= 4)
    45. {
    46. printf("请输入两个操作数\n");
    47. scanf("%d %d", &x, &y);
    48. ret = pfarr[input](x, y);
    49. printf("ret=%d\n", ret);
    50. }
    51. else
    52. {
    53. printf("选择错误,重新选择!\n");
    54. }
    55. } while (input);
    56. return 0;
    57. }

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

    1. int Add(int x, int y);
    2. int Sub(int x, int y);
    3. int Mul(int x, int y);
    4. int Div(int x, int y);
    5. int main()
    6. {
    7. int a = 10;
    8. int b = 20;
    9. int c = 30;
    10. int* arr[] = { &a,&b,&c };//整型指针数组
    11. int* (*p)[3] = &arr;//p是指针,是指向整形指针数组的指针
    12. int (*pfArr[5])(int, int) = { NULL,Add,Sub,Mul,Div };//pfArr是函数指针数组
    13. int(*(*p)[5])(int,int) = &pfArr;//p是存放函数指针数组的指针
    14. return 0;
    15. }

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

    举例:
    利用函数指针,实现回调函数,解决了代码的冗余

    1. void menu()
    2. {
    3. printf("*****************************************\n");
    4. printf("************ 1.add 2.sub ***********\n");
    5. printf("************ 3.mul 4.div ***********\n");
    6. printf("************ 0.exit ***********\n");
    7. printf("*****************************************\n");
    8. }
    9. int Add(int x, int y)
    10. {
    11. return x + y;
    12. }
    13. int Sub(int x, int y)
    14. {
    15. return x - y;
    16. }
    17. int Mul(int x, int y)
    18. {
    19. return x * y;
    20. }
    21. int Div(int x, int y)
    22. {
    23. return x / y;
    24. }
    25. void calc(int (*pf)(int, int))//函数指针
    26. {
    27. int x = 0;
    28. int y = 0;
    29. int ret = 0;
    30. printf("请输入两个操作数\n");//没有直接调用函数,将地址传给这个函数,在这个函数内部调用
    31. scanf("%d %d", &x, &y);//这里的加减乘除就是回调函数
    32. ret = pf(x, y);//调用函数并返回
    33. printf("ret = %d\n", ret);
    34. }
    35. int main()
    36. {
    37. int input = 0;
    38. do
    39. {
    40. menu();
    41. printf("请选择:>\n");
    42. scanf("%d", &input);
    43. switch (input)
    44. {
    45. case 1:
    46. calc(*Add);
    47. break;
    48. case 2:
    49. calc(*Sub);
    50. break;
    51. case 3:
    52. calc(*Mul);
    53. break;
    54. case 4:
    55. calc(*Div);
    56. break;
    57. case 0:
    58. printf("退出计算器\n");
    59. break;
    60. default:
    61. break;
    62. }
    63. } while (input);
    64. return 0;
    65. }

    回调函数案例:qsort函数
    qsort是一个库函数
    底层使用的是快速排序的方式,对数据进行排序
    这个函数可以直接使用
    这个函数可以用来排序任意类型的数据

    对数据进行排序:
    冒泡排序 
    快速排序
    选择排序
    插入排序

    1. void bubble_sort(int arr[], int sz)
    2. {
    3. int i = 0;
    4. //比较的趟数 n个元素要进行n-1次冒泡排序 n个元素有n-1对元素要进行排序
    5. for (i = 0; i < sz - 1; i++)
    6. {
    7. //每一趟冒泡排序
    8. int j = 0;
    9. for (j = 0; j < sz-1-i; j++)
    10. {
    11. if (arr[j] > arr[j + 1])
    12. {
    13. int t = 0;
    14. t = arr[j];
    15. arr[j] = arr[j + 1];
    16. arr[j + 1] = t;
    17. }
    18. }
    19. }
    20. }
    21. void print_arr(int arr[],int sz)//这个函数只能排序整形数据
    22. {
    23. int i = 0;
    24. for (i = 0; i < sz; i++)
    25. {
    26. printf("%d ", arr[i]);
    27. }
    28. printf("\n");
    29. }
    30. int main()
    31. {
    32. int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
    33. int sz = sizeof(arr) / sizeof(arr[0]);
    34. print_arr(arr, sz);
    35. bubble_sort(arr, sz);//冒泡排序
    36. print_arr(arr, sz);
    37. return 0;
    38. }

    ***************************qsort函数举例*****************************//
    qsort函数的使用方法:
    回忆冒泡排序的算法
    给一组整形数据,使用冒泡排序算法,拍成升序
    核心思想:相邻两组元素进行比较
    n个元素要进行n-1次冒泡排序 n个元素有n-1对元素要进行排序

    1. int cmp_int(const void* e1, const void* e2)
    2. {
    3. return *(int*)e1 - *(int*)e2;//强制转换为整形指针
    4. //compare函数返回值-1 0 1
    5. }
    6. void print_arr(int arr[], int sz)//这个函数只能排序整形数据
    7. {
    8. int i = 0;
    9. for (i = 0; i < sz; i++)
    10. {
    11. printf("%d ", arr[i]);
    12. }
    13. printf("\n");
    14. }
    15. //void qsort(void* base,//指针待排序数组的第一个元素的地址
    16. // size_t num,//待排序数组元素的个数
    17. // size_t size,//待排序数组中一个元素的大小
    18. // int(*cmp)(const void* e1, const void* e2)//函数指针名-cmp指向了一个函数,这个函数是用来比较两个元素大小
    19. // );
    20. // e1和e2中存放的是需要比较的两元素的地址
    21. //将比较方法的函数抽离出来,最后根据需求结合
    22. //1.排序整型数组,两个整形可以直接使用>比较
    23. //2.排序结构体数组,两个结构体的数据可能不能直接使用>比较
    24. //也就是不同类型的数据,比较大小,方法是有差距的
    25. //测试qsort排序整形数据
    26. void test1()
    27. {
    28. int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
    29. int sz = sizeof(arr) / sizeof(arr[0]);
    30. print_arr(arr, sz);
    31. qsort(arr, sz, sizeof(arr[0]), cmp_int);
    32. print_arr(arr, sz);
    33. }
    34. //测试qsort排序结构体数据
    35. //结构体
    36. //1.按照年龄比较
    37. //2.按照名字比较
    38. struct Stu
    39. {
    40. char name[20];
    41. int age;
    42. };
    43. //1.按照年龄比较
    44. int cmp_stu_by_age(const void* e1, const void* e2)
    45. {
    46. return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
    47. }
    48. int cmp_stu_by_name(const void* e1, const void* e2)
    49. {
    50. return strcmp(((struct Stu*)e1)->name ,((struct Stu*)e2)->name);
    51. }
    52. void test2()
    53. {
    54. struct Stu arr[] = { {"zhangsan",20},{"lisi",30},{"wangwu",12} };
    55. int sz = sizeof(arr) / sizeof(arr[0]);
    56. qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);
    57. }
    58. void test3()
    59. {
    60. struct Stu arr[] = { {"zhangsan",20},{"lisi",30},{"wangwu",12} };
    61. int sz = sizeof(arr) / sizeof(arr[0]);
    62. qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);
    63. }
    64. int main()
    65. {
    66. test1();
    67. //test2();
    68. //test3();
    69. return 0;
    70. }

    *****************************\n***************************//
    void* 类型的指针,他不能进行解引用操作,也不能进行+-整数的操作
    void* 类型的指针可以用来存放任意类型数据的地址
    void* 是无具体类型的指针,用来接收任意类型的地址

    1. int main()
    2. {
    3. char c = 'w';
    4. char* pc = &c;
    5. int* p = &c;
    6. void* pv = &c;
    7. int a = 100;
    8. pv = &a;
    9. return 0;
    10. }


    ........

  • 相关阅读:
    iOS全埋点解决方案-采集崩溃
    IoT 边缘集群基于 Kubernetes Events 的告警通知实现(二):进一步配置
    相控阵天线(二):非规则直线阵列天线(稀布阵列、稀疏阵列、平方率分布阵列、含python代码)
    Git分布式版本控制系统——git学习准备工作
    页面分页打印,echarts图解决办法;生成PDF
    Ubuntu上安装和配置MySQL
    2023高教社杯数学建模E题思路模型 - 黄河水沙监测数据分析
    ORA-22922:不存在的 LOB 值
    24.Xaml ListView控件-----显示数据
    NFT,还是有未来的
  • 原文地址:https://blog.csdn.net/m0_73983707/article/details/132837477