zoukankan      html  css  js  c++  java
  • 指针理解——指针数组、数组指针、指针函数、函数指针

    一个存在已久的谣言

    源码

    #include <stdio.h>
    int main()
    {
         int ar[10] = { 1,2,3,4,5,6,7,8,9,10 };
         printf("&ar[0]=%p
    ",&ar[0]);
         printf("ar=%p
    ", ar);
         printf("&ar=%p
    ", ar);
         getchar();
         return 0;
    }

    运行结果:

    根据运行结果,很多人就会得出“数组名就是首元素的地址”这样错误的结论。见代码

    #include <stdio.h>
    int main()
    {
         int ar[10] = { 1,2,3,4,5,6,7,8,9,10 };
         printf("&ar[0]=%p
    ",&ar[0]);
         printf("ar=%p
    ", ar);
         printf("&ar=%p
    ", ar);
         printf("sizeof(ar)=%d
    ", sizeof(ar));
         printf("sizeof(&ar[0])=%d
    ", sizeof(&ar[0]));
         getchar();
         return 0;
    }

    运行结果:

    如果 “数组名就是首元素的地址” 结论属实,那么数组名的大小就是一个指针的大小。事实上,数组名代表整个数组空间。

    数组名(ar)本身的确是个地址,在数值上等于数组首元素取地址(&ar[0]),等于对数组名取地址(&ar)。数值上这三个数相等,那只是表象。其实质是地址背后指向内存空间的能力是不同的。

    这里面还有个有趣的问题,就是数组作为形参会退化为指针。参考C++——数组形参退化为指针。这也就是为啥,数组作为形参的时候还要再多给一个数组长度参数。

    数组指针、指针数组

    数组名是地址,与数组首元素地址仅代表自己类型那么大内存不同,数组名内存指向能力非常强。数组名指向整个数组空间。进一步讲,对数组名取地址,即就是在对整个数组取地址,则数组的地址自然要用指向数组的指针才能接收,所以,必须定义指向数组的指针类型,即为数组指针。见代码

    #include <stdio.h>
    int main()
    {
         int ar[10] = { 1,2,3,4,5,6,7,8,9,10 };
         int *p = ar;
      
         /*下面是错误代码
         int **p = &ar;*/
     
         //正确写法
         int(*pp)[10] = &ar;
         getchar();
         return 0;
    }

    int *p = ar;  p的类型int *,p指向的类型int

    int **p = &ar;  p的类型int** ,p指向的类型int*

    int ar[10]的类型,int[10]

    int(*pp)[10] = &ar;pp的类型是 int (*)[10],pp指向int [10]类型

    数组指针、指针数组重点在于后两个字。即数组指针本质上是指针,指针数组本质上是数组。这是字面上理解,代码角度怎么区分呢?

    int ar[10] = { 1,2,3,4,5,6,7,8,9,10 };
    int(*p1)[10] = &ar;      //数组指针
    int* p2[3];              //指针数组

    变量(这里是p1,p2)与[ ]优先结合,所以int* p2[3];是指针数组,是一个数组。 要想变成指针,需要使用 ()强制优先结合指针。

    总结:

    数组指针

    首先它是一个指针,它指向一个数组,在32位系统下永远是占4个字节,至于它所指向的数组占多少字节是不知道的,它是“指向数组的指针”简称

    对于数组指针,强调的是指针的概念,只不过,指针的能力是用来指向数组类型的,并且其方括号中的数字一定,例如:int (*p)[10],p就是指向数组的指针,其中p指针规定了只能指向整形的数组,并且数组大小只能是10个整形空间,不能多也不能少,多之少之都会认为其指针的能力与指向的实体不符。

    指针数组

    指针数组,首先它是一个数组,数组的元素都是指针,数组占多少字节由数组本身决定,它是“储存指针的数组”的简称。

    对于指针数组,强调的是数组的概念,只不过,数组所保存的类型是指针罢了,其地位跟普通的数组没有什么区别,都是数组,只不过是大家保存的类型不同而已,因此,我们美名其曰:保存指针的数组就称其为指针数组, 例如:int *p1[10]

    指针数组最典型的例子就是main函数:int main(int argc,char *argv[])  第二个参数是一个字符串指针数组

    #include <stdio.h>
    int main()
    {
         int ar[3] = { 1,2,3 };
         //数组指针指向ar
         int (*p)[3] = &ar;
          //指针数组
         int* q[3] = {&ar[0],&ar[1],&ar[2]};
          char* pstr[] = { "Hello","halo","nihao" };
        getchar();
         return 0;
    }

    函数指针、指针函数

    函数指针

    函数指针,首先它是一个指针,只不过,指针所指向的类型是函数,它是“指向函数的指针”的简称

    #include <stdio.h>
    int Max(int a, int b)
    {
        printf("%d
    ", a > b ? a : b);
         return a > b ? a : b;
     }
    
    //函数指针
    int(*pfun)(int, int);
     
    void main()
    {
         //情形1
         Max(1, 2);
     
         //情形2
         int(*pfun)(int, int);
         pfun = &Max;
         (*pfun)(1, 2);
     
         //情形3
         pfun = Max;
         pfun(1, 2);
     
         getchar();
    }

    一般来说,我们调动函数往往是通过函数名来进行调动,例如情形1,由于指针强悍与无所不能,只要你能表示出来的,指针都可以想办法指向,因此,通过指针来调动函数就显得很自然了,我们把能够指向函数的指针称为函数指针,例如情形2,把Max函数的地址赋给了pfun函数指针,在调动时先取值,然后再调动函数,这是一种标准的做法,事实上,由于函数名就是函数的入口地址,本身也充当了地址,因此,我们可以简化程序,例如情形3,由于指针所指之物为函数,因此它的调动就行如直接运行函数类似了,但是,心里的清楚,情形3的做法实际是情形2的简写过程。

    注意,一般指针都有其加1的能力,但是,函数指针不允许做这样的运算。即pfun+1是一个非法的操作。

    指针函数

    指针函数,首先它是一个函数,只不过,函数所返回的类型是指针类型,它是“返回指针类型的函数”的简称。 我们把返回指针类型的函数称其为指针函数,那就意味着只要返回值为指针,无论是什么类型的指针,都有资格称为指针函数

    //指针函数,返回整形指针
    int* fun(int a, int b)
    {
        return 0;
    }

    像这种,函数fun,参数是(int a, int b),返回值是int* 。这种比较明显

    返回函数指针的指针函数

    先看一种错误的写法。对于VS2012以后的IDE,这种代码写得时候直接显示红色〰,根本编译不过

    #include <stdio.h>
    int Max(int a, int b)
    {
          printf("%d
    ", a > b ? a : b);
        return a > b ? a : b;
    }
    
     //指针函数,返回函数指针。但是这种写法是错误的
    int(*) (int,int) func(int a, int b, int(*FUN)(int, int))
    {
         printf("max value=%d
    ", FUN(a, b));
        return FUN;
    }
    
    void main()
    {
         func(1, 2, Max);
        getchar();
    }

    正确写法

    #include <stdio.h>
    int Max(int a, int b)
    {
         printf("%d
    ", a > b ? a : b);
        return a > b ? a : b;
    }
     
    //函数指针
    int(*pfun)(int, int);
     
    //func这个函数参数是(int a, int b, int(*FUN)(int, int))
    //返回值是个指针,这个指针是int (*) (int, int)型函数指针
    int(*func(int a, int b, int(*FUN)(int, int))) (int, int)
    {
         printf("max value=%d
    ", FUN(a, b));
         return FUN;
    }
    
    void main()
    {
         func(1, 2, Max);
         getchar();
    }

     但是这种代码写出来太难理解了,可以使用typedef简化代码

    #include <stdio.h>
    int Max(int a, int b)
    {
         printf("%d
    ", a > b ? a : b);
         return a > b ? a : b;
    }
    
    //将函数指针定义成类型
    typedef int(*pfun)(int, int);
     
    //func这个函数参数是(int a, int b, int(*FUN)(int, int))
    //返回值是个指针,这个指针是int (*) (int, int)型函数指针
    //int(*func(int a, int b, int(*FUN)(int, int))) (int, int)
    pfun func(int a, int b, pfun FUN)
    {
        printf("max value=%d
    ", FUN(a, b));
        return FUN;
    }
    
    void main()
    {
         func(1, 2, Max);
         getchar();
    }
  • 相关阅读:
    Echarts数据可视化grid直角坐标系(xAxis、yAxis)
    Cannot find module '@babel/plugin-proposal-class-properties'
    git 基本操作
    React报错:Laravel React-Router browserHistory 刷新子页面报错404
    php报错:Notice: iconv(): Wrong charset, conversion from `GBK' to `UTF8' is not allowed
    Larval报错:后台上传图片,storage目录也有相应的图片,但前台访问不到图片。
    Laravel报错:1071 Specified key was too long; max key length is 1000 bytes
    thinkcmf报错:fileowner(): stat failed for /sys
    vbox虚拟机复制&&虚拟机指定静态IP
    thinkcmf在万网上部署
  • 原文地址:https://www.cnblogs.com/kelamoyujuzhen/p/9351241.html
Copyright © 2011-2022 走看看