• 【C语言】进阶——指针


     

    目录

    ①(●'◡'●)前言

    1.字符指针 

    ✌字符指针和数组笔试题 

    2.指针数组 和数组指针

    👊指针数组 

    👊数组指针 

     👊&数组名和数组名

    3.数组传参和指针传参 

    👊一维数组传参

    👊二维数组传参

     👊一级指针传参

    👊二级指针传参

    4.函数指针 

    5.函数指针数组

    👊函数指针数组应用 

    6.函数指针数组的指针

    7.回调函数 

    👊qsort() 

    冒泡排序通用版


     

    ①(●'◡'●)前言

    在之前【C语言】入门——指针介绍了指针的概念

    1. 指针就是个变量,用来存放地址,地址唯一标识一块内存空间。

    2. 指针的大小是固定的4/8个字节(32位平台/64位平台)

    3. 指针是有类型,指针的类型决定了指针的+-整数的步长,指针解引用操作的时候的权限。

    4. 指针的运算。

    这一篇介绍更深度的指针内容 

    1.字符指针 

    步长最短的字符型指针,字符指针就是用来存放字符变量(或字符串变量)的指针

    当存储字符串变量时,会像数组一样只存入首字母的地址,在解引用时可以根据首地址依次往后访问并解引用,直到遇到结束标志 '\0'

    由此看来指针貌似和数组有点相似。

    1. int main()
    2. {
    3. char s1 = 'a';
    4. char* p = &s1;
    5. char* p2 = "abcdef";
    6. printf("%c\n", *p);
    7. printf("%s\n", p2);
    8. return 0;
    9. }

     

    ✌字符指针和数组笔试题 

    1. //字符指针笔试题
    2. int main()
    3. {
    4. char arr1[] = { "Hello World" };
    5. char arr2[] = { "Hello World" };
    6. const char* str1 = "Hello World";
    7. const char* str2 = "Hello World";
    8. if (arr1 == arr2)
    9. printf("arr1 and arr2 are same\n");
    10. else
    11. printf("arr1 and arr2 are not same\n");
    12. if (str1 == str2)
    13. printf("str1 and str2 are same\n");
    14. else
    15. printf("str1 and str2 are not same\n");
    16. return 0;
    17. }

    arr1和arr2是两个独立的数组,自然地址不一样,独立空间,所以不相等;

    str1和str2是因为指向同一块空间,因为两个相同的常量在内存中只会开辟一块空间; 

     

     

    2.指针数组 和数组指针

    指针数组是数组,数组内存放的是指针;

    数组指针是指针,存放数组的地址。

    数组指针与指针数组容易混淆

    int arr[5];

    arr是一个数组,每个元素是int类型的 ,有5个元素

    int* parr1[10];

    parr1是一个数组,数组10个元素,每个元素的类型是int*

    int (*parr2)[10];

    parr2是一个指向数组的指针,指向的数组有10个元素,每个元素的类型是int

    int(* parr3[10])[5];

    parr3是一个数组,数组有10个元素,每个元素的类型是:int(*)[5]

    parr3是存放数组指针的数组 

    👊指针数组 

    1. //指针数组
    2. int main()
    3. {
    4. int a = 1, b = 2, c = 3;
    5. int* pa = &a;
    6. int* pb = &b;
    7. int* pc = &c;
    8. int* arr[3] = { pa,pb,pc };
    9. int i = 0;
    10. for (i = 0; i < 3; i++)
    11. {
    12. printf("地址:%p\n", arr[i]);
    13. printf("值为:%d\n", *arr[i]);
    14. }
    15. return 0;
    16. }

     

     定义三个变量,取各自他们的地址赋给不同的指针,指针为int*,再将指针放到数组内存放,

    &arr的类型为:int* (*arr)[3];

    arr的类型为int* arr;

    👊数组指针 

    1. //数组指针
    2. #include <stdio.h>
    3. int main()
    4. {
    5.     int arr[10] = { 0 };
    6.     int (*p)[10] = &arr;//数组指针,存放整形数组arr的地址
    7.     return 0;

     👊&数组名和数组名

    1. //&数组名与数组名
    2. int main()
    3. {
    4. int arr[5] = { 1,2,3,4,5 };
    5. int(*pa)[5] = &arr;
    6. printf("这是起始地址:%p %p\n", arr, pa);
    7. printf("这是+1后的地址:%p %p\n", arr + 1, pa + 1);
    8. return 0;
    9. }

     

    &arr+1,加一个步长,但是是整个数组的地址+1,所以是跳过整个数组大小;

    数组5个元素,0x00fbfe58是首元素地址,58-5c-60-64-68-6c;

    跳过了整个数组大小20个字节;

    arr+1; 首元素地址+1,是跳过一个元素大小; 

    58-5c;整型数组,一个整型4个字节,跳过一个元素即是4个字节;

    3.数组传参和指针传参 

    👊一维数组传参

    1. //一维数组传参
    2. void test1(int arr[])
    3. {}//一维数组可以省略元素数,也可以写上
    4. void test1(int*pa)
    5. {}//用一级指针接收一维数组
    6. void test2(int*arr2[10])
    7. {}//形参用指针数组接收指针数组传参
    8. void test2(int**ppa)
    9. {}//指针数组本质上是二级指针
    10. int main()
    11. {
    12. int arr1[10] = { 0 };
    13. int* arr2[10] = { 0 };
    14. test1(arr1);
    15. test2(arr2);
    16. return 0;
    17. }

    👊二维数组传参

    三种方式:

    完整传参,行和列都不省略;

    省略行;

    数组指针接受

    1. //二维数组传参
    2. void test(int arr[3][5])
    3. {}//完整接收
    4. void test(int arr[][5])
    5. {}//省略行,是可以的
    6. void test(int(*pa)[5])
    7. {}//用我们前面的数组指针接收
    8. void test(int** pa)
    9. {}//是错误的
    10. int main()
    11. {
    12. int arr[3][3] = { 0 };
    13. test(arr);
    14. return 0;
    15. }

     👊一级指针传参

    1. //一级指针传参
    2. void test1(int* pa, int sz)
    3. {}//传数组名,用指针接收
    4. void test2(int* pa, int sz)
    5. {}//传指针,用指针接收
    6. int main()
    7. {
    8. int arr[3] = { 1,2,3 };
    9. int sz = sizeof(arr) / sizeof(arr[0]);
    10. int* pa = arr;
    11. test1(arr, sz);
    12. test2(pa, sz);
    13. return 0;
    14. }

    👊二级指针传参

    1. //二级指针传参
    2. void test1(int**pa)
    3. {}//接收的是二级指针
    4. void test2(int**pa)
    5. {}//接收的一级指针的地址
    6. int main()
    7. {
    8. int a = 10;
    9. int* pa = &a;//一级指针
    10. int** ppa = &pa;//二级指针
    11. test1(ppa);//传二级指针
    12. test2(&pa);//将一级指针的地址取出来
    13. return 0;
    14. }

    4.函数指针 

    函数指针是指向函数的指针;函数名就是地址

    函数指针由三部分组成:

    类型、指针、形参,

    类型和形参可以为空,当想要调用函数时,只需要通过指针,并传入参数,就能正常使用函数。 

    1. int Add(int x,int y)
    2. {
    3. return x + y;
    4. }
    5. int main()
    6. {
    7. int (*p)(int, int) = &Add;
    8. printf("%d", p(2, 3)); //5
    9. return 0;
    10. }

     来个函数指针趣题

    1. void (*signal(int, void(*)(int)))(int);

    1.函数指针: void(*)(int) 

    2.函数名: signal

    3.signal函数的参数: int,  void(*)(int);   一个整型,一个函数指针

    4.返回值:signal函数的返回值是指针 

    5.函数指针数组

     将一些函数地址存入数组中,就得到了函数指针数组。

    1. //函数指针数组
    2. int Add(int x, int y)
    3. {
    4. return x + y;
    5. }
    6. int Sub(const int x, const int y)
    7. {
    8. return x - y;
    9. }
    10. int main()
    11. {
    12. int(*pfun[2])(const int x, const int y) = { Add,Sub };
    13. printf("%d\n", pfun[0](2, 3)); //5
    14. printf("%d\n", pfun[1](5, 3)); //2
    15. return 0;
    16. }

    函数名就是地址名,不需要&地址,函数形参不能省略 

    👊函数指针数组应用 

    1. //简易整型计算器
    2. #include<stdio.h>
    3. void menu()
    4. {
    5. printf("*****计算器*****\n");
    6. printf("**1.Add 2.Sub**\n");
    7. printf("**3.Mul 4.DIV**\n");
    8. printf("*****0.exit*****\n");
    9. }
    10. int add(const int x, const int y)
    11. {
    12. return x + y;
    13. }
    14. int sub(const int x, const int y)
    15. {
    16. return x - y;
    17. }
    18. int mul(const int x, const int y)
    19. {
    20. return x * y;
    21. }
    22. int div(const int x, const int y)
    23. {
    24. return x / y;
    25. }
    26. int main()
    27. {
    28. int input = 1;
    29. int(*calc[5])(const int x, const int y) = { 0,add,sub,mul,div };
    30. //0的原因是和菜单中的序号对应上
    31. while (input)
    32. {
    33. menu();
    34. printf("请输入:>");
    35. scanf("%d", &input);
    36. if (input > 0 && input < 5)
    37. {
    38. int x = 0, y = 0;
    39. printf("请输入两个数:");
    40. scanf("%d %d", &x, &y);
    41. printf("计算结果为%d\n", calc[input](x, y));
    42. }
    43. else if (input >= 5)
    44. printf("选择错误,请重新选择!\n");
    45. }
    46. printf("退出计算器\n");
    47. return 0;
    48. }

     

     

    6.函数指针数组的指针

     套娃式,本质是指针;

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

     例子2:

    1. //函数指针数组的指针
    2. int add(int x, int y)
    3. {
    4. return x + y;
    5. }
    6. int main()
    7. {
    8. //这是函数指针数组
    9. int (*pa[5])(int x, int y) = { add };
    10. //这是函数指针数组的指针,需要取出地址
    11. int(*(*ppa)[5])(int x, int y) = &pa;
    12. printf("这是函数指针数组的指针%p\n", ppa);
    13. printf("这是&函数指针数组后的地址%p\n", &pa);
    14. return 0;
    15. }

    7.回调函数 

    回调函数就是一个通过函数指针调用的函数。

    如果你把函数的指针(地址)作为参数传递给另一个 函数,当这个指针被用来调用其所指向的函数时,这就是回调函数。

    (依赖函数指针,有了函数指针,才能实现回调函数) 

    👊qsort() 

    qsort():快速排序

    库函数,头文件#include

    qsort函数可以进行各种数据的排序

     

    void qsort(void* base,                 //待排序数组的第一个元素的地址

    size_t num,                                 //待排序数组的元素个数

    size_t size,                                //待排序数组中一个元素的大小

    int (* cmp)(const void* e1, const void* e2)        //函数指针,自己定义排序函数

    整型实例 

    1. //qsort()
    2. #include<stdio.h>
    3. #include<stdlib.h>
    4. int cmp(const void* e1, const void* e2)
    5. {
    6. return *(int*)e1 - *(int*)e2;
    7. }
    8. int main()
    9. {
    10. int arr[] = {5,4,3,2,1 };
    11. int sz = sizeof(arr) / sizeof(arr[0]);
    12. qsort(arr, sz, sizeof(arr[0]), cmp);
    13. int i = 0;
    14. for (i = 0; i < sz; i++)
    15. {
    16. printf("%d ", arr[i]);
    17. }
    18. return 0;
    19. }

    函数指针-cmp指向了一个函数,这个函数是用来比较两个元素的

    e1和e2中存放的是需要比较的两个元素的地址

    void* 不能直接进行计算,需要强转成其他类型;

    结构体示例 

    1. #include<string.h>
    2. #include<stdlib.h>
    3. struct Stu
    4. {
    5. char name[20];
    6. int age;
    7. };
    8. int cmp_by_name(const void* e1,const void* e2)
    9. {
    10. return strcmp(((struct Stu*)e1)->name,((struct Stu*)e2)->name);
    11. }
    12. int main()
    13. {
    14. struct Stu arr[] = { {"zhangsan",20},{"wangwu",15} };
    15. //元素个数
    16. int sz = sizeof(arr) / sizeof(arr[0]);
    17. qsort(arr, sz, sizeof(arr[0]), cmp_by_name);
    18. return 0;
    19. }

    strcmp()是根据字符字典顺序比较的,后面的大,需要包含头文件#include 

    qsort函数中就用到了回调函数的知识,使用qsort,它都会去调用比较函数。 

    冒泡排序通用版

    用冒泡排序的思路去模仿qsort(); 

    1. #include<string.h>
    2. struct Stu
    3. {
    4. char name[20];
    5. int age;
    6. };
    7. //结构体年龄比较
    8. int cmp_age(void* e1, void* e2)
    9. {
    10. return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
    11. }
    12. //字节交换,size是一个数据的字节大小
    13. void swap(char* buf1, char* buf2, size_t size)
    14. {
    15. int k = 0;
    16. for (k = 0; k < size; k++)
    17. {
    18. char temp = *buf1;
    19. *buf1 = *buf2;
    20. *buf2 = temp;
    21. buf1++;
    22. buf2++;
    23. }
    24. }
    25. //整型比较
    26. int cmp_int(const void* e1, const void* e2)
    27. {
    28. return *(int*)e1 - *(int*)e2;
    29. }
    30. //打印
    31. void print(int* arr, int sz)
    32. {
    33. int i = 0;
    34. for (i = 0; i < sz; i++)
    35. {
    36. printf("%d ", arr[i]);
    37. }
    38. }
    39. //冒泡模拟qsort
    40. void my_qsort(void* base, size_t num, size_t size, int(*cmp_name)(const void* p1, const void* p2))
    41. {
    42. int i = 0;
    43. for (i = 0; i < num - 1; i++)
    44. {
    45. int j = 0;
    46. for (j = 0; j < num - 1 - i; j++)
    47. {
    48. if (cmp_name((char*)base + j * size, (char*)base + (j + 1) * size) > 0) //得到的返回值,如果大于0,就交换
    49. {
    50. swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
    51. }
    52. }
    53. }
    54. }
    55. int main()
    56. {
    57. struct Stu arr[] = { {"zhangsan",15},{"lisi",12},{"wangwu",30} };
    58. int iarr[] = { 9,8,7,6,5,4,3,2,1,0 };
    59. //整型数组的元素个数
    60. int isz = sizeof(iarr) / sizeof(iarr[0]);
    61. my_qsort(iarr, isz, sizeof(iarr[0]), cmp_int);
    62. print(iarr, isz);
    63. //结构体的元素个数
    64. int sz = sizeof(arr) / sizeof(arr[0]);
    65. my_qsort(arr, sz, sizeof(arr[0]), cmp_age);
    66. return 0;
    67. }

    感谢你看到这里

    以上就是我对进阶指针的介绍,身为初学者,自知有很多不足和需要改善的地方,希望大佬们指点一二,感激不急!!!

    ⭐愿星光照亮每一位赶路人 ⭐

     

  • 相关阅读:
    Docker Compose
    Elasticsearch6.2服务器升配后的bug
    极简试用期转正述职报告PPT模板
    STM32使用ThreadX示例以及tx_thread_create解析
    安装TimeGen波形绘图软件
    应用案例|基于三维机器视觉的曲轴自动化上下料应用方案
    DQL数据查询语句之ORDER BY排序查询示例
    C++前缀和算法的应用:石头游戏 VIII 原理源码测试用例
    int类的前置++和后置++的实现
    【电力系统】基于粒子群算法优化电力系统潮流计算附matlab代码
  • 原文地址:https://blog.csdn.net/m0_67367079/article/details/132919508