小工具知识网

当前位置: 首页 > 行业知识 > 那些年与指针的情仇(二)---二级指针&&指针与数组的那点事&&函数指针

行业知识

那些年与指针的情仇(二)---二级指针&&指针与数组的那点事&&函数指针

2026-11-04 23:44:26 来源:互联网转载

关���小庄 顿顿解馋(。・∀・)ノ゙

欢迎回到我们的大型纪录片《那些年与指针的爱恨情仇》,在本篇博客中我们将继续了解指针的小秘密:二级指针,指针与数组的关系以及函数指针。请放心食用!


文章目录

  • 一. 二级指针
  • 二. 数组与指针的那点事儿
    • 1. 数组名的理解
      • 1.1 数组名本质理解
      • 1.2 sizeof数组名和取地址数组名
      • 2. 指针数组
      • 3. 字符串常量
      • 4. 数组指针
      • 5. 数组传参
      • 三. 函数指针

        一. 二级指针

        前面我们讲到了指针变量是个存储指针(地址)的变量,我们知道变量在创建的时候操作系统会给他分配内存空间同时给他编号(地址),那么指针变量的指针(地址)能否被存储呢?这里就引入我们二级指针的概念了

        二级指针:存储指针变量地址的指针

        地址变量
        0x0012ffaaa
        0x00134455*p = &a
        0x0012aaff**pp=&p

        *在上面表格中pp变量其实就是对应的二级指针,我们可以通过双重解引用pp来找到a,*pp = p, *(*pp)=a

        注:二级指针类型定义时的*理解逻辑可以类比我们之前理解指针变量类型的定义,二级指针变量前的*说明这是个指针(存储地址的小子),而前面的int*说明它指向的对象是int *类型的,他的变量类型是int **

        二. 数组与指针的那点事儿

        1. 数组名的理解

        1.1 数组名本质理解

        int arr[10] = {1,2,3,4,6,7,8,9,10};printf("&arr[0] = %p\n", &arr[0]);printf("arr = %p\n", arr);

        输出结果

        &arr[0] = 004F9CC

        arr = 004F9CC

        这里我们可以得出数组名本质是首元素的地址,明白这个后我们以后访问数组就可以用指针的方式le

        int arr[10]={1,23,4,5,6,7,8,9,10};int* p = arr;for(int i = 0;i  

        延伸:*此时p与arr等价->arr[i] = *(p+i)= *(arr+i) *

        那是否所有情况下都是这个样子呢?嘿嘿,我们看下面的代码

        1.2 sizeof数组名和取地址数组名

        Sizeof数组名

        int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };printf("%d\n", sizeof(arr));

        输出结果 : 40

        看到这里有同学会疑惑,数组名本质不是首元素地址吗?

        小庄如是说:没错,但是这是一种特殊情况也就是特例,sizeof中单独放数组名,这⾥的数组名表⽰整个数组,计算的是整个数组的⼤⼩,单位是字节

        &数组名

        //代码1int arr[10] = {1,2,3,4,5,6,78,9,10};printf("%p\n",&arr[0]);printf("%p\n",&arr);printf("%p\n",arr);//代码2printf("&arr[0]+1 = %p\n", &arr[0]+1);printf("arr+1 = %p\n", arr+1);printf("&arr+1 = %p\n", &arr+1);

        代码1输出结果:

        0x004FC550 //&arr[0]

        0x004FC550 //&arr

        0x004FC550 //arr

        代码2输出结果:

        0077F824 //&arr[0] +1

        0077F824 //arr+1

        0077F848 //&arr+1

        我们可以发现代码1输出结果相同,代码2就出现不同了其中&arr[0]和arr的结果相同这就再次验证了数组名的本质是首元素的地址,但对于&数组名+1,&arr和&arr+1相差40个字节,这就是因为&arr是数组的地址,+1操作是跳过整个数组的!

        因此我们可以得出&数组名,这里的数组名表示整个数组,取出的是整个数组的地址,只不过数组都是从首个元素开始的。相当于你拿一箱水果只能一个一个搬,而比你壮的可以整箱搬起来

        总结:对于数组名的运用,sizeof+数组名和&+数组名是特例表示整个数组地址,其他地方都是首元素地址

        2. 指针数组

        抛砖引玉:前面我们学习了数组的相关知识,整形数组是存储整型数据的数组,字符数组是存储字符型数据的数组… 那指针数组呢?

        指针数组:存储指针的数组,数组每个元素都是来存储指针(地址)的,主体是数组.

        • 语法形式

          Datatype * arr[size]

          这里的Datatype*指的是数组存储元素的类型,也就是arr是一个存储整型指针的数组,大小为size。

          • 指针数组模拟二维数组
            int arr1[] = {1,2,3,4,5};int arr2[] = {6,7,8,9,10};int arr3[] = {11,12,13,14,15};int* arr[3] = {arr1,arr2,arr3};int i,j;for(i=0;i   for(j=0;j   printf("%d ",arr[i][j]);   }   printf("\n");}//我们取每个整形数组首元素地址存储到指针数组中,就可以依靠这个首元素地址顺藤摸瓜到整个数组//从而打印出每行实现二维数组的效果'g','o','o','d',''};int sz2 = sizeof(arr)/sizeof(arr[0]);printf("sz2 = %d\n", sz2);}int main(){int arr[10] = {1,2,3,4,5,6,7,8,9,10};int sz1 = sizeof(arr)/sizeof(arr[0]);printf("sz1 = %d\n", sz1);test(arr);return 0;}printf("hehe\n");}int main(){printf("test: %p\n", test);printf("&test: %p\n", &test);return 0;}return a + b;}int sub(int a, int b){return a - b;}int mul(int a, int b){return a*b;}int p(int a, int b){return a / b;}int main(){int x, y;int input = 1;int ret = 0;int(*p[5])(int x, int y) = { 0, add, sub, mul, p }; //转移表do{printf("*************************\n");printf(" 1:add 2:sub \n");printf(" 3:mul 4:p \n");printf(" 0:exit \n");printf("*************************\n");printf( "请选择:" );scanf("%d", &input);if ((input printf( "输⼊操作数:" );scanf( "%d %d", &x, &y);ret = (*p[input])(x, y);printf( "ret = %d\n", ret);}else if(input == 0){printf("退出计算器\n");}else{printf( "输⼊有误\n" );}}while (input);return 0;}

上一篇:揭秘冷的英语表达秘籍,如何准确翻译冷字?

下一篇:服务器怎么得知?