堆栈与C中的堆指针
我目前正在学习C语言,并对内存布局和指针感到困惑。
在下面的代码中,我的理解是数组在堆栈上分配。
#include <stdio.h>
int main () {
int x[4];
x[0] = 3; x[1] = 2; x[2] = 1;
printf("%pn",x);
printf("%pn", &x);
}
我的问题是,为什么这两个打印调用输出相同的值?
我尝试了一个类似的片段使用malloc(分配在堆上),并且值不同。
#include <stdio.h>
#include <stdlib.h>
int main () {
int *x = malloc(sizeof(int) * 4);
x[0] = 3; x[1] = 2; x[2] = 1;
printf("%pn",x);
printf("%pn", &x);
}
原因是,与你可能被教导的不同,数组不是指针。 在某些情况下,C中的数组衰减为指针1。 当你将一个数组传递给一个函数时,它会衰减成一个指向第一个元素的指针。 该元素的地址与整个数组的地址相同(地址始终是对象的第一个字节)。
你从malloc
得到的不是一个数组,而是一块内存的地址。 您将地址分配给指针。 但是指针和块是独立的实体。 因此,打印指针的值,而不是其地址,会产生不同的结果。
(1)衰减是一种隐式类型转换的奇特术语。 当大多数地方使用数组表达式时(例如作为参数传递给需要指针的函数)时,它会自动变成指向其第一个元素的指针。 “衰减”是因为您丢失了类型信息,即数组大小。
您的两个打印调用会打印相同的值,因为您尝试打印数组,该数组衰减为指向数组的指针,另一个打印数组的地址。 指向数组的指针包含数组的地址,所以它们的值相同。
在第二种情况下,一个打印x
的值,另一个打印x
的地址。 由于x
是指向您分配的内存块的指针,因此这些值必须是不同的值。
所以在第一种情况下,你所拥有的只是一个数组( x
)。 在第二种情况下,您有一个分配的内存块(未命名)和一个指向该分配块( x
)的指针。
也许令人惊讶的是,人们确实可以接受整个阵列的演讲,部分原因是不需要经常演讲。 从某种意义上说,数组是一个单一的对象,它有一个地址,它是第一个字节的地址。 与所有对象一样,地址通过地址运算符&
。
数组的第一个元素(就像它的所有元素一样)也有一个地址,也就是它的第一个字节的地址。 指向其第一个元素的指针是数组类型在作为参数传递给函数时被“调整”的数值。
这两个字节是相同的,并且具有相同的地址。 但是它们有不同的类型,如果给它们加1并再次打印它们就会变得很明显。
相反,指针y
是它自己独特的对象(大小可能是4或8个字节;足以存储一个地址)。 像任何对象一样,它有一个可以用&
运算符获得的地址。 也许令人困惑的是,它也包含一个地址,在这种情况下是数组的第一个字节的地址。 这两者当然不相等:指针对象驻留在与数组不同的位置(即在堆栈旁边,即使Olaf不喜欢这样)。
小调:你用%p
打印指针,这很好。 如果你这样做了,你应该严格地将你打印的指针转换成一个void指针: printf("%pn", (void *)x);
。