我目前正在学习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
得到的不是数组,而是内存块的地址。将地址分配给指针。但是指针和块是独立的实体。因此,打印指针的值,而不是它的地址,会产生不同的结果。
您的两个print调用打印相同的值,因为其中一个试图打印数组,数组会衰减为指向数组的指针,而另一个打印数组的地址。指向数组的指针包含数组的地址,因此它们是相同的值。
在第二种情况下,一种打印x
的值,另一种打印x
的地址。由于x
是指向您分配的内存块的指针,因此这些值必须不同。
所以在第一种情况下,您所拥有的只是一个数组(x
)。在第二种情况下,您有一个已分配的内存块(未命名)和一个指向该已分配块的指针(x
)。
确实可以获取整个数组的地址,这可能令人惊讶,部分原因是不需要经常这样做。从某种意义上说,数组是一个对象,它有一个地址,即它的第一个字节的地址。与所有对象一样,地址是通过地址运算符&
获得的。
数组的第一个元素(像它的所有元素一样)也有一个地址,这是它的第一个字节的地址。指向其第一个元素的指针是数组类型作为参数传递给函数时"调整"到的值。
这两个字节是相同的,并且具有相同的地址。但它们有不同的类型,如果你在它们上加1并再次打印,这一点就会变得显而易见。
相比之下,指针y
是它自己的不同对象(大小可能为4或8个字节;足以在其中存储地址)。像任何对象一样,它有一个可以用&
运算符获得的地址。也许令人困惑的是,它还包含一个地址,在本例中是数组第一个字节的地址。这两者当然是不相等的:指针对象位于与数组不同的位置(即堆栈上它的旁边,即使Olaf不喜欢这样)。
备注:您使用%p
打印指针,这很好。如果您这样做,您应该严格地将打印的指针强制转换为空指针:printf("%pn", (void *)x);
。