• C语言 指针进阶 壹


    头文件

    1. #define _CRT_SECURE_NO_WARNINGS 1
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include

    内存单元有编号 编号 = 地址 = 指针
    指针就是个变量,用来存放地址,地址唯一标识一块内存空间
    地址/指针/指针变量的大小是4 / 8个字节(32位/64位平台)
    指针类型决定了指针加一减一的步长 指针解引用操作的权限
    指针的运算

    1.字符指针
    把一个字符的地址赋值给指针
    字符串表达式的值是首字符的地址

    1. int main()
    2. {
    3. char ch = 'w';
    4. char* pc = &ch;
    5. char arr[] = "abcdef";
    6. const char* p = "abcdef";
    7. printf("%p\n", p);
    8. char* p1 = arr;//数组的地址
    9. char* p2 = &arr[0];//数组首元素的地址
    10. printf("%p\n", p1);//000000906378FAD4
    11. printf("%p\n", p2);//000000906378FAD4
    12. printf("%c\n", "abcdef"[3]);//d 想象成地址,访问数组中下标为3的元素
    13. //首字符a的地址赋给了p
    14. return 0;
    15. }
    1. int main()
    2. {
    3. char str1[] = "hello bit.";
    4. char str2[] = "hello bit.";
    5. const char* str3 = "hello bit.";//const+变量:该变量里的数据只能被访问,不能被修改,意味只读
    6. const char* str4 = "hello bit.";
    7. if (str1 == str2)//not same 两个数组地址不相同
    8. printf("same\n");
    9. else
    10. printf("not same\n");
    11. if (str3 == str4)//same 两个常量字符串比较 常量字符串不能被修改 内容相同时,只会保存一份
    12. printf("same\n");
    13. else
    14. printf("not same\n");
    15. if (&str3 == &str4)//no
    16. printf("same\n");
    17. else
    18. printf("not same\n");
    19. return 0;
    20. }

    字符串≈数组
    常量字符串只能保存一份  地址不同 但是只保存一份常量字符串 所以两字符串很像

    2.指针数组——是数组

    存放在数组中的元素是指针类型
    整形数组是存放整形的数组
    字符数组是存放字符的数组
    指针数组是存放指针的数组
    存放在数组中的元素是指针类型的

    int* arr[5]存放整形指针的数组

    char* ch[6]存放字符指针的数组

    1. int main()
    2. {
    3. int a = 1;
    4. int b = 2;
    5. int c = 3;
    6. int d = 4;
    7. //不会这样使用
    8. int* arr[] = { &a,&b,&c,&d };
    9. return 0;
    10. }

     用指针数组模拟二维数组的

    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. }
    1. int main()
    2. {
    3. //指针数组的应用场景之一
    4. const char* arr[5] = { "hello bit ","hehe ","zezeze ","ijij ","666 " };
    5. for (int i = 0; i < 5; i++)
    6. {
    7. printf("%s\n", arr[i]);
    8. }
    9. return 0;
    10. }

    指针数组可以维护多个数组
    指针数组是一种数组形式的存在

    3.数组指针是指针,指向数组的指针
      指针数组是数组,是存放指针的数组

    1. int main()
    2. {
    3. int a = 10;
    4. int* p1 = &a;
    5. char ch = 'c';
    6. char* p2 = &ch;
    7. float f = 3.14;
    8. float* p3 = &f;
    9. return 0;
    10. }

    数组名的理解 : 数组名是数组首元素的地址

    2个例外
    sizeof(数组名)表示整个数组,计算整个数组大小,单位字节
    & 数组名,这里的数组表示整个数组,表示整个数组的大小
    p = &arr
    arr & arr & arr[0] 取地址相同 它们的值是一样的

    1. int main()
    2. {
    3. int arr[10];
    4. printf("arr: %p\n", arr);
    5. printf("arr+1:%p\n", arr+1);
    6. printf("\n");
    7. printf("&arr: %p\n", &arr);//它们的值相同
    8. printf("&arr+1:%p\n", &arr+1);//它们的值相同
    9. printf("\n");
    10. printf("&arr[0]: %p\n", &arr[0]);
    11. printf("&arr[0]+1:%p\n", &arr[0]+1);
    12. //指针类型决定了指针+1,到底+几个字节
    13. return 0;
    14. }

    前两个加一是类型不同 跳过字节
    第三个跳过整个数组元素的字节

    数组指针的创建方式 : int(*p)[10] = &arr p是用来存放数组的地址的 是数组指针

    1. int main()
    2. {
    3. int arr[10] = { 0 };
    4. //数组指针的写法:
    5. int (*p)[10] = &arr;//p是用来存放数组的地址的,p就是数组指针
    6. //eg、1
    7. char* arr1[5];
    8. char* (*pc)[5] = &arr1;
    9. //eg、2
    10. int* arr2[7];
    11. int* (*p)[7] = &arr2;
    12. return 0;
    13. }

    数组指针的用处 :

    1. int main()
    2. {
    3. //法1:
    4. int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    5. int(*p)[10] = &arr;
    6. for (int i = 0; i < 10; i++)
    7. {
    8. printf("%d ", (*p)[i]);
    9. }
    10. return 0;
    11. }
    1. int main()
    2. {
    3. //法2: 一维数组正确访问方式:
    4. int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    5. int* p = arr;
    6. int i = 0;
    7. for (i = 0; i < 10; i++)
    8. {
    9. printf("%d ", p[i]);
    10. }
    11. return 0;
    12. }

    遍历二维数组中的每一个元素 

    1. void print(int arr[3][5], int r, int c)
    2. // (int (*P)[5],int r,int c) 二维数组传参 形参的部分写的是指针
    3. {
    4. for (int i = 0; i < 3; i++)
    5. {
    6. for (int j = 0; j < 5; j++)
    7. {
    8. printf("%d ", arr[i][j]);
    9. }
    10. printf("\n");
    11. }
    12. }
    13. int main()
    14. {
    15. int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
    16. print(arr, 3, 5);//数组名,行数,列数 二维数组传参
    17. return 0;//arr是二维数组的数组名,是首元素的地址,
    18. //二维数组首元素的地址是第一行的地址,第一行的地址是第一行首元素的地址
    19. }

    数组传参本质是传指针 

    1. void print(int *arr, int sz)//形式参数指针的形式 本质是指针
    2. //void print(int arr[], int sz)
    3. {
    4. int i = 0;
    5. for (i = 0; i < sz; i++)
    6. {
    7. printf("%d ", arr[i]);//arr[i]===>*(arr+i)
    8. }
    9. }
    10. int main()
    11. {
    12. int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
    13. int sz = sizeof(arr) / sizeof(arr[0]);
    14. print(arr, sz);
    15. //一维数组传参
    16. return 0;
    17. }
    1. int arr[5];//arr是一个能够存放5个整形数据的数组
    2. int* parr1[10];//parr1是一个数组,数组10个元素,每个元素的类型是int*
    3. int(*parr2)[10];//parr2是一个数组指针,该指针指向数组,指向的数组有10个元素,每个元素的类型是int
    4. int(*parr3[10])[5];//parr3是一个数组,是存放数组指针的数组,存放的这个数组指针,指向的数组有5个元素,每个元素是int类型

    替换二维数组传参 形参使用二维数组的形式
    二维数组行可以省略 列不能省略 一维数组可以省略
    一维数组传参用指针传参更好理解
    二维数组数组名是第一行的地址 第一行的地址是第一行一维数组的地址
    二维数组传参 形参的部分传给指针


    4.数组参数和指针参数
    把数组传参给指针
    一维数组 : 数组传参 形参的部分可以写成数组形式


    数组传参时 数组的大小随意
    数组传参的本质是传递数组首元素的地址
    数组传参 形参可以传为指针形式

    1. void test(int arr[])
    2. {}y
    3. void tst(int arr[10])
    4. {}y
    5. void test(int* arr)
    6. {}y
    7. void test2(int *arr[20])
    8. {}y
    9. void test2(int **arr)//传过去一级指针的地址,所以可以写成二级指针
    10. {}y
    11. 数组名表示首元素的地址 每个元素都是int * 类型地址 数组名表示首元素的地址是int * 地址
    12. 二维数组传参
    13. void test(int arr[3][5])
    14. {}y
    15. void test(int arr[][])
    16. {}N 行可以省略,列不可以省略
    17. void test(int arr[][5])
    18. {}y
    19. void test(int *arr)
    20. {}N
    21. void test(int* arr[5])
    22. {}N 要么写成指针,要么写成数组
    23. void test(int (*arr)[5])
    24. {}y
    25. void test(int **arr)
    26. {}N
    27. 二维数组传参 : 行可以省略 列不能省略
    28. 数组名传参传地址传的是第一行的地址

     一级指针传参

    1. void print(int* p, int sz)
    2. {
    3. int i = 0;
    4. for (i = 0; i < sz; i++)
    5. {
    6. printf("%d ", *(p + i));
    7. }
    8. }
    9. int main()
    10. {
    11. int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    12. int* p = arr;
    13. int sz = sizeof(arr) / sizeof(arr[0]);
    14. //一级指针p,传给函数
    15. print(p, sz);
    16. return 0;
    17. }

    二级指针用来接收一级指针的地址
    一级指针传参
    传地址和传指针一样 形参部分写成一级指针便可
    一级指针用来接收地址
    看类型的地址 同一类型的指针都可以传参

    当一个函数的参数部分为一级指针的时候,函数能接收什么参数

    1. void test(int* p)
    2. {
    3. }
    4. int main()
    5. {
    6. int a = 10;
    7. int* ptr = &a;
    8. int arr[5];
    9. test(arr);//传整形一维数组的数组名
    10. test(&a);//传整型变量的地址
    11. test(ptr);//传整形指针
    12. }

    二级指针传参
    先给一个一级指针变量
    再把一级指针的地址传给二级指针
    二级指针传参只能用二级指针的地址传参
     

    1. #include
    2. void test(int** ptr)
    3. {
    4. printf("num = %d \n", **ptr);
    5. }
    6. int main()
    7. {
    8. int n = 10;
    9. int* p = &n;
    10. int** pp = &p;
    11. //二级指针变量等于一级指针变量的地址
    12. //当一个二级指针变量int **p接收,可以传一个一级指针变量地址,可以传一个二级指针变量
    13. //还可以传一个数组首元素的地址 如:int* arr[6]
    14. test(pp);
    15. test(&p);
    16. return 0;
    17. }

    数组指针 : 指向数组的指针 存放的是数组的地址 取地址数组名就是数组的地址 &数组名就是数组的地址
    函数指针 : 指向函数的指针 存放的是函数的地址 取地址函数名就是函数的地址 &函数名是函数的地址吗
     

    1. int Add(int x, int y)
    2. {
    3. return x + y;
    4. }
    5. int main()
    6. {
    7. //&函数名和函数名代表的都是函数的地址
    8. int a, b;
    9. printf("请您输入两个值\n");
    10. scanf("%d%d", &a, &b);
    11. int c = Add(a, b);
    12. printf("%p\n", &Add);
    13. printf("%p\n", Add);
    14. printf("%d", c);
    15. int (*pf1)(int, int) = Add;
    16. int (*pf2)(int, int) = &Add;
    17. //int* pf2(int,int);函数的声明
    18. //由此可见类型相同,两种写法皆可
    19. int ret = (*pf2)(2, 3);
    20. int ret1 = Add(2, 3);
    21. printf("%d\n", ret);
    22. printf("%d\n", ret1);
    23. return 0;
    24. }

    函数名也是函数的地址
    与数组指针写法非常类似
    函数指针也是一种指针 是函数的地址 需要一个函数的变量

    1. //代码1
    2. int main()
    3. {
    4. //void(*)()函数指针类型
    5. //int a=(int)3.14;
    6. //下面的代码是在调用0地址处的函数,这个函数没有参数,返回类型是void
    7. ( *(void (*)( ))0)();
    8. //返回类型void 强转 调用函数无参数
    9. }
    1. //代码2:
    2. int main()
    3. {
    4. //对一个类型重新起名
    5. //typedef unsigned int uint
    6. //类型重命名 复杂类型 重命名
    7. void(* signal(int, void(*)(int)))(int);
    8. //void(*)(int) signal(int, void(*)(int));//不支持
    9. //signal函数 两参数:int类型 函数指针类型 参数是int,返回类型是void
    10. //这个代码是一次函数声明,声明的是signal函数,
    11. //signal函数参数有两个,第一个是int类型,第二个是函数指针类型,该类型是void(*),
    12. //signal函数的返回类型也是函数指针类型,该类型是void(*)(int)
    13. //该函数指针指向的函数,参数是int,返回类型是void
    14. return 0;
    15. }

    代码2太复杂,如何优化:
    typedef void(*pfun_t)(int);
    pfun_t signal(int, pfun_t);

    typedef unsigned int uint
    类型重命名 复杂类型 重命名
     

    1. typedef unsigned int uint;//类型重命名
    2. int main()
    3. {
    4. uint a;
    5. unsigned int b;
    6. return 0;
    7. }

  • 相关阅读:
    计算机毕业设计之java+javaweb的影院管理系统-电影院管理系统
    基于JavaScript开发的网页多点绘图程序
    Three导入dae格式模型实例
    嵌入式开发需要掌握的u-boot命令
    【故障分类】基于注意力机制的卷积神经网络结合双向长短记忆神经网络CNN-BiLSTM-attention实现数据分类附matlab代码
    【数据聚类】基于Baysian、KNN、3Layer Neural Network Classifier、KMeans多种算法实现数据聚类附matlab代码
    mysql全文索引
    查词翻译类应用使用数据接口api总结
    Java 函数式接口、lambda表达式、初识Stream
    【JetCache】JetCache的使用方法与步骤
  • 原文地址:https://blog.csdn.net/m0_73983707/article/details/132792404