2024年10月16日日发布:c语言那些事儿:指针数组和数组指针傻傻分不清?

发布日期:2024-10-15 24:19

来源类型:第一财经 | 作者:张娣

【澳门特一码资料期期准】【今晚开什么特马好】【今晚特马多少号 查询】【香港免费资料六典大全】【4949澳门今晚结果】【澳门一肖一码100准免费资料澳门肖】【女人长期跑步的好处】【小龙人论坛资料大全最新版】【澳门天天免费资料大全下载】【2024年澳门精准免费大全】
【白姐一肖一码结果】 【新澳六开彩号码】 【四六好彩7777788888】

原本在我看来,“指针数组”和“数组指针”是两个并不相近的知识点。因为前一个的“主体”是数组,后一个的“主体”是指针,主体都不同。

虽然说数组可以看作是被const修饰的指针(常量类型的指针),但是这仅仅是为了理解方便,数组是分配在栈(stack)上的,而指针指向的内存,一般都是动态分配的,更多的是在堆(heap)上,使用起来要加倍小心,更为复杂(指针当然也可以指向栈上的数据)。

但是前几天我写了一篇剖析“常量指针”和“指针常量”的文章,在几个平台上发布之后,有一些朋友给我私信说,能不能写写“指针数组”和“数组指针”,也做一个“深度的解剖”,所以才有了今天这篇文章。

指针数组,其实说详细点,就是数组元素的类型是“指针”的数组。因为指针这个概念是不能独立存在的,必须要和具体的数据类型绑定在一起才有意义。所以,假设这里的指针是int型指针,那么我们可以这样定义:

int* array[3];

数组的名字是array,数组元素的类型是 int型指针(int*),这个数组一共有3个元素,没有被初始化。这个数组因为它的元素都是指针,我们称为“指针数组”。

总结一下,指针数组首先得是一个数组,然后它的元素类型一定是指针,满足这个条件就可以确定是“指针数组”了,只是要注意定义的格式,以免写错。

我们简单的举个例子,来对这个数组进行操作。

int* array[3];
int x,y,z;
array[0] = &x;
array[1] = &y;
array[2] = &z;
for(int i=0;i<3;i++){
	*array[i] = i * i ;
	printf("%d ",*array[i]);
}

这行代码的输出结果如下图:

程序运行的结果

首先定义一个指针数组和三个变量,然后将变量的地址赋值给数组的元素。最后通过数组元素间接的给变量赋值,并演示了通过数组元素显示变量的“值”,核心在于始终记得数组元素就是一个指针就可以了。

另外,指针数组可以是任意维的数组,比如我们定义一个二维的指针数组,并且对第三个元素赋值(变量的地址):

int* array[][3];
int x = 3;
array[0][2] = &x;

二维数组概念此处不展开,因为后面数组指针里要说到。

数组指针,说详细点,就是指向某种类型的数组的指针。核心在于这次不是数组,而是指针。只是这个指针指向的是“数组”。因为数组和指针一样,不是一种数据结构,必须要和某个数据类型绑定在一起才有意义,假设这个数组是int型数组,那么我们可以这样定义:

int (*pointer)[3];

这样写,确实看起来很别扭。如果改成这样:int[3] (*pointer);我们可能看的更明白些。

int[3] 是数组类型,有3个元素,(*ponter)是指针,和 int[3]结合起来,就是指向int[3]整型数组的pointer指针了。但是很遗憾,c语言不支持这样的写法

我再次强调一下,“数组指针”,是一个指针,这个指针特别的地方在于它不是指向一个普通的变量,而是指向一个数组结构(对,不是数据结构)。

也就是说它的“值”是一个数组的地址。通常我们都把数组名作为数组的地址对待,或者数组首元素的地址作为数组的地址对待,这两者在一般情况下是没有区别的(仅在一般情况下,此处不展开)。

因此,我们很容易就可以写出给数组指针赋值的语句,比如用数组名给数组指针pointer赋值:

int (*pointer)[3];
int array[3];
pointer = array;

或者,将数组首元素的地址赋值给数组指针pointer:

int (*pointer)[3];
int array[3];
pointer = &array[0];

当然也可以给数组指针pointer赋值一个匿名数组:

int (*pointer)[3];
pointer = (int[]){1,2,3};

无论如何,我们的数组指针已经指向了一个“数组”。那么我们如何通过数组指针来访问它说指向的数组的每一个元素呢?

我们尝试着这样:

int (*poiter)[3];
int array[3];
pointer = array;
pointer[0] = 1;//error

我们认为pointer = array; 说明pointer和array其实指向了同一个地址,那么通过指针pointer访问array的第一个元素,和array去访问第一个元素的用法应该一样才对啊。但为什么array[0]可以,pointer[0]不可以呢?

实际上,你应该能想到,不可以才是正确的,因为假设可以,那么下面这段代码:

int array[3];
int *ptr = array;
ptr[0] = 1;

ptr去访问array的元素是可以的,实际上此时的ptr和array就是等价的,如果上面的pointer也可以这样,那么就意味着pointer和ptr是等价的,因为pointer和array等价,ptr和array也等价。

可是这样一来,就出现了悖论,因为一个是指向“数组”的指针,一个是指向“普通变量”的指针,明显不同的指针类型,怎么可能等价呢?

我想这大概才是很多人迷惑的原因吧!

实际上,在之前的一些文章里,我提到过,这里的ptr虽然可以被数组名直接赋值,但实际上它指向的实际上不是数组,而是数组的第一个元素的地址。这有什么不同呢?

我其实不想把一个问题解剖的太细,因为这样会增加复杂度,但是在这里有必要稍微详细说一下。

数组名的“值”和数组第一个元素的地址的“值”看起来是同一个值,也可以相互混用去给别的指针赋值,但是实际上他们代表的含义是完全不同的。

数组名代表的是整个数组的地址,比如这个数组的元素是3个int型,假设在你的系统环境里int占据4个字节,那么数组名实际上代表的是占据了12个字节的这个内存块的地址。

而数组首个元素的地址,代表的是这个元素在内存中所占据的4个字节的内存地址。只是他们两者的“值”是相同的,所以我们就经常混用,而不加以区分了。

所以,你应该意识到 ptr = array;不是准确的写法。正确的写法是 ptr = &array[0]; 因为ptr它是一个指向“普通变量”的指针,而array[0]是一个普通变量,array不是,他是很多相同类型的普通变量组合成的一个“特殊”类型。

感谢你看到这里,你真的很有耐心。

因此,pointer指向的是这样一种情况:一个内存块是由一个或多个数组组成的,而pointer指向了这个内存块的地址(就是首地址)。

假设这个内存块由两个数组组成,其实就是一个“二维数组”。比如:

int two_col_array[2][3];
int (*pointer)[3];
pointer = two_col_array;

那么,显而易见pointer[0]其实就是two_col_array第一个数组的地址,pointer[1]其实就是two_col_array第二个数组的地址。

那么下面这几条语句都是等价的:

pointer = two_col_array;
pointer = two_col_array[0];
pointer[0] = two_col_array;
pointer[0] = two_col_array[0];

当这个内存块只有一个数组时,实际上就是一个一维数组,比如:

int array[3];
int (*pointer)[3];

那么以此类推,下面这几条语句也是等价的:

pointer = array;
pointer = &array[0];
pointer[0] = array;
pointer[0] = &array[0];

只是在二维数组中,其中某个一维数组的名字仍然是地址,而在一维数组中,只有pointer[0],没有pointer[1]而已。

所以回到最初,当我们想通过pointer修改指向的数组的元素的值时,我们只要把pointer看成是一个“二维数组的名字”就可以了。比如:

int array[3];
int (*pointer)[3] = array;
pointer[0][0] = 99;
pointer[0][1] = 520;
pointer[0][2] = 1314;

我们通过pointer把数组array的3个元素分别赋了值。

我们再举一个略显复杂的例子来看一看(没用循环赋值,是为了把二维数组的特点体现的更明显一点):

int a,b,c,d;
int* p_array[][2] = {{&a,&b},{&c,&d}};
int* (*pointer)[2] = p_array;
*pointer[0][0] = 1;
*pointer[0][1] = 2;
*pointer[1][0] = 3;
*pointer[1][1] = 4;
for(int i=0;i<2;i++){
		for(int j=0;j<2;j++){
			printf("%d ",*pointer[i][j]);
	} 
}

pointer指向的是一个二维数组,这个二维数组是一个指针数组,这个例子是为了演示数组指针和指针数组在一起时该如何使用。

如果你对二维数组稍微了解些的话,你就已经意识到了所谓的数组指针其实和二维数组是很相似的。

学习编程的最好办法,除了多写代码,就是将近似的知识点在一起比较分析,这是最快的学习方式。

今天这个主题就讲到这里,其实还有很多可以展开的知识点,比如二维数组在内存中仍然是顺序存放的,以此类推,无论多少维的数组,在内存中其实都是线性存储的。

由此可见,多维数组只是我们逻辑上的一种表示方法,并不是物理上的存储方法。那么,数组指针是不是就可以和二维数组一样去使用呢?他们有没有异同?等等,感兴趣的朋友可以继续深挖。

另外,我的文章里的示例代码都没介绍编译器和系统环境,因为太多了,我经常换着用,所以很难每次都罗列一下,所以大家编译时有差异,并且困惑的时候,可以私信我交流沟通。

段誉,2024.1.29,写于合肥。

【2024新澳门天天开好彩大全资料】【澳门一肖一码免费知资料】【管家婆一马中一肖2024】【今晚必中几号特马】【2024年新澳门精准资料】【新澳门最快结果】【王中王一肖一特一中】【2024澳门免费精准资料网站】【澳门六开彩结果免费】【打开澳门免费资料大全六】
【2024新澳门正版免费资料】 【香港今晚六给彩结果16期】 【澳门天天开彩结果记录】

出租车囧事:

9秒前:int *ptr = array;

艾琳·特策尔:

3秒前:int[3] 是数组类型,有3个元素,(*ponter)是指针,和 int[3]结合起来,就是指向int[3]整型数组的pointer指针了。

刘贵生:

3秒前:int x = 3;

马丁·本森:

9秒前:因此,我们很容易就可以写出给数组指针赋值的语句,比如用数组名给数组指针pointer赋值:int (*pointer)[3];

潘永:

4秒前:所以回到最初,当我们想通过pointer修改指向的数组的元素的值时,我们只要把pointer看成是一个“二维数组的名字”就可以了。

泪泪:

7秒前:pointer = array;或者,将数组首元素的地址赋值给数组指针pointer:int (*pointer)[3];

程耀华:

1秒前:可是这样一来,就出现了悖论,因为一个是指向“数组”的指针,一个是指向“普通变量”的指针,明显不同的指针类型,怎么可能等价呢?