抛出问题
最近碰到一个问题点,这里跟大家分享一下。有一个二维数组,我想把它传给一个函数。于是我把函数接口定义出来了,如下:
int array[2][3] = {1,2,3,4,5,6};void fun(int **array) { array[0][0] = 5;}
当我试图直接把数组名传给函数时候,fun(array)编译会报错,大概意思就是类型不匹配。既然类型不匹配,那我就直接强转成你所需要的类型,于是我又做了调整,fun((int **)array),这下确实不报错了。但是此时我还没意识到问题的严重性。不出意外的情况下意外还是发生了,只要进入到这个函数后,程序就挂了。那你知道是什么原因吗?如果不清楚就往下看吧...
指针
先从指针说起,指针是一个特殊的变量, 它里面存储的数值被解释成为内存里的一个地址。要搞清一个指针需要搞清指针的四方面的内容:指针的类型、 指针所指向的类型 、 指针的值或者叫指针所指向的内存区、 指针本身所占据的内存区。
指针的类型
只要把指针声明语句里的指针名字去掉,剩下的部分就是这个指针的类型。例如:
int*ptr; //指针的类型是 int*char*ptr; //指针的类型是 char*int**ptr; //指针的类型是 int**int(*ptr)[3]; //指针的类型是 int(*)[3]
指针所指向的类型
只须把指针声明语句中的指针名字和名字左边的指针声明符*去掉, 剩下的就是指针所指向的类型。例如:
int*ptr; //指针所指向的类型是 intchar*ptr; //指针所指向的的类型是 charint**ptr; //指针所指向的的类型是 int*int(*ptr)[3]; //指针所指向的的类型是 int()[3]
指针的值
在32位程序里,所有类型的指针的值都是一个32位整数,因为32位程序里内存地址全都是32位长。
指针本身所占用的内存大小
意思是指针本身占了多大的内存,在32位平台里,指针本身占据了4个字节的长度。可以使用sizeof(指针的类型)测试。
一维数组
对于一个一维数组int array[10],数组名代表一个常量地址,该地址指向第一个元素。以下两种情况数组名不能当指针使用。
&
对数组名取址,int *p_array = &array,&这个运算符也很有讲究的,暂时不多说了。
sizeof
sizeof(array)计算的是整个数组在内存中所占用的空间。
二维数组
二维数组本质上是以数组作为数组元素的数组,即“数组的数组”。假设我们定义了一个二维数组int array[2][3] = {1,2,3,4,5,6}。
网上有很多地方都再说数组名array和array[0]、&array[0]以及&array[0][0]是等效的。那我们代码测试一下。
printf("%#x,%#x,%#x,%#x\r\n",array,array[0],&array[0],&array[0][0]);Terminal:0x404008,0x404008,0x404008,0x404008
因为这几种写法输出地址都是相同的,所以有的同学自然就认为这几种写法就是一样的。虽然地址相同,但是实际意义是有区别的,我们继续看下面的代码。
//这里重新定义了指针变量,能够方便的知道右值得类型int *p_array1 = array[0];int *p_array2 = &array[0][0];int (*p_array3)[3] = &array[0];int (*p_array4)[3] = array; printf("%#X,%#X,%#X,%#X,%#X\r\n",array,++p_array1, ++p_array2, ++p_array3, ++p_array4);Terminal:0X404008,0X40400C,0X40400C,0X404014,0X404014
根据以上实验分析能够看出:array[0]与&array[0][0]指针类型相同,都是int *,地址存放的是int数据,当指针自增1时地址都偏移了一个int类型的大小。
&array[0]与array指针类型相同,都是int (*)[3],首先它是一个数组指针,这个指针指向一个数组,数组中数据的类型为int型。当指针自增1时地址都偏移了一个数组的长度(即3个int数据的大小)。
所以说array只和&array[0]真正意义等效。那怎么去理解这几种表达呢 ?
表示 | 含义 |
array | 是一个数组指针,类型为int (*)[3]。指向二维数组中第一个元素(元素是一维数组),指针所指向的内存大小为一维数组的长度 |
array[0] | 是一个指针,类型为int *。就相当于一个一维数组名,指向一维数组中第一个元素的地址,指针所指向的内存大小为一个数据长度 |
&array[0] | 是一个数组指针,类型为int (*)[3]。相当于对一维数组取地址。指针所指向的内存大小为一维数组的长度 |
&array[0][0] | 是一个指针,类型为int *,是对二维数组中第一个数据取地址,注意是数据不是元素,指针所指向的内存大小为一个数据长度 |
如以上能够理解清楚,那么文中的问题应该就能够自己分析清楚了。
二级指针
先定义一个二级指针int **p,首先p是一个指针,在这个地址中存放的数据是指向一个整形数据的地址。
问题解答
接着看文章中的问题,把一个二维数组强转成二级指针传给了函数。注意二维数组名的类型是一个数组指针和二级指针完全不是一个东西。那么会出现什么问题呢?
int array[2][3] = {1,2,3,4,5,6};int main(int argc ,char **argv) { int **p_data = (int **)array; printf("%#x, %d\r\n", p_data, *p_data);}Terminal:0x404008, 1
地址 | 数据 |
0x404008 | 1 |
0x40400C | 2 |
0x404010 | 3 |
0x404014 | 4 |
0x404018 | 5 |
0x40401C | 6 |
看上面的例子,array的地址为0x404008,当把一个二维数组强转成二级指针的时候。p_data地址中存放的数据为1,因为二维数据中第一个数据就是1。根据二级指针的定义,这个数据1又会当成一个地址,该地址指向的内存才是最终的数据。
但是呢,这个地址1其实是个数据,并不是真正的地址。如果访问地址1中的数据,就属于非法访问地址了,可能会进入异常。
二维数据当函数入参
通过以上学习我们已经知道二维数组名就是一个数组指针,我们函数就可以像下面这样声明。
void fun(int array[][3], int row);void fun(int (*p_array)[3], int row);void fun(int row, int column, int array[row][column]);
实参与入参
最后在看下,应该如何定义与实参相对应地形参的数据类型。
含义 | 实参 | 形参 |
二维数组(数组的数组) | int array[4][6] | int (*array)[6] |
指针数组(数组中的数据是指针) | int *array[6] | int **array |
数组指针(指向数组的指针) | int (*array)[6] | int (*array)[6] |
二级指针(指针的指针) | char **array | char **array |
版权声明:内容来源于互联网和用户投稿 如有侵权请联系删除