Fork me on GitHub

指针理解——指针数组、数组指针、指针函数、函数指针

一个存在已久的谣言

源码

#include <stdio.h>
int main()
{
     int ar[10] = { 1,2,3,4,5,6,7,8,9,10 };
     printf("&ar[0]=%p\n",&ar[0]);
     printf("ar=%p\n", ar);
     printf("&ar=%p\n", 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\n",&ar[0]);
     printf("ar=%p\n", ar);
     printf("&ar=%p\n", ar);
     printf("sizeof(ar)=%d\n", sizeof(ar));
     printf("sizeof(&ar[0])=%d\n", 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\n", 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\n", 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\n", FUN(a, b));
    return FUN;
}

void main()
{
     func(1, 2, Max);
    getchar();
}

正确写法

#include <stdio.h>
int Max(int a, int b)
{
     printf("%d\n", 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\n", FUN(a, b));
     return FUN;
}

void main()
{
     func(1, 2, Max);
     getchar();
}

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

#include <stdio.h>
int Max(int a, int b)
{
     printf("%d\n", 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\n", FUN(a, b));
    return FUN;
}

void main()
{
     func(1, 2, Max);
     getchar();
}
posted @ 2018-07-22 20:19  克拉默与矩阵  阅读(3454)  评论(0编辑  收藏  举报