C中的数组大小和地址

时间:2009-09-26 19:23:54

标签: c memory

当我编译下面的代码时,它显示y和数组的开头相隔60个单位。但根据我的计算,它应该是4 * 10(对于数组)+ 4(对于k)+ 4(对于y)= 48。 同样,数组[12] = 17被分配给元素12,因为没有元素12,实现应该转到y并用17覆盖y。但控制台打印y = 10而不是...我真的很困惑...请救命!

 #include <stdio.h>

 int main(void) {
    int x = 42;
    int y = 10;
    int k = 10;
    int array[10];

    array[12] = 17;
    printf("array starts at %d\n", &array[0]);
    printf("x has the value %d\n", x);
    printf("x is stored in location %d\n", &x);

    printf("y has the value %d\n", y);
    printf("y is stored in location %d\n", &y);
 }

12 个答案:

答案 0 :(得分:7)

这称为未定义行为(在10个元素数组中写入数组[12]),因此根据定义,您无法知道应该做什么。在C中没有运行时检查,所以你可以写到你想要的任何地方(好吧,为了这个例子),你不知道究竟会发生什么。

答案 1 :(得分:5)

当我编译此代码(OSX上的gcc)时,它告诉我y和您的array相隔8个字节。这正是我所期待的......当地人正在这样布局:

int x = 42;
int y = 10;
int k = 10;
int array[10];

0                   1                   2
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3...etc.
\__,__/ \__,__/ \__,__/ \__,__/ \__,__/ \__,__/
   x       y       k      [0]     [1]     [2]
                          array

数字是堆栈框架底部的偏移量。所以x填充底部的4个字节,'y'堆叠在它的顶部,依此类推。该数组长40个字节,位于最顶层。

&yy的起始地址,它是堆栈帧底部的+4个字节。 &array[0]是数组起始地址,距堆栈帧底部+12个字节。

顺便说一句,你应该把你的地址格式化为'unsigned' - %u。没有它,大地址可能会出现负数。

array[12]超出了所有这一切,所以你不应该指望它会影响y。如果您想要使用未定义的行为,那么array[-2]可能等同于y。为了让它像这样工作,我必须编译优化 - 你的里程可能会有所不同:)

答案 2 :(得分:3)

C不保证您在函数顶部定义的变量在内存中一个接一个地排列,因此您无法假设变量k的位置与位置有关内存中的数组。

它是60而不是48可能是因为编译器以某种方式对齐内存中的数据(即它故意跳过一些字节)以使CPU更有效地访问数据(CPU可以从内存中获取数据)例如,如果它是8或16字节的倍数更快,它的工作原理取决于你的CPU的细节。但实际上,k和数组之间的距离也可能相差一百万字节。

请注意,您的数组包含10个值。尝试访问0到9范围之外的元素是不对的,就像你正在做的那样(array[12])。 C不检查数组边界,但如果访问索引无效的数组,可能会发生奇怪的事情 - 程序可能崩溃或产生不可预测的结果。

答案 3 :(得分:2)

&amp; x是变量“x”的地址,与您的数组没有任何关系,只是它恰好在附近的堆栈上分配。

要获取数组中“xth”项的地址,您需要使用&amp; array [x]

array [12]不在数组的末尾,所以你要覆盖堆栈上的另一个变量。这可能意味着另一个变量(例如y)被覆盖,或者它可能导致程序的灾难性崩溃,具体取决于存储在该locaton的内容。您应该只在10个元素的数组中访问成员数组[0]到数组[9]。

答案 4 :(得分:2)

C并不保证程序与您编写的一样。它只保证喜欢你写的东西。编译器可以移动和优化代码的变量,只要它不会改变代码的功能。

  • 代码的某些变量可能存储在只读内存中,甚至不在堆栈中。

  • C不保证堆栈以相反的顺序使用内存。

答案 5 :(得分:2)

无法保证您的变量在内存中以任何特定的关系相互排列。尝试更改数组[12]是未定义的行为。它可能会改变y,它可能会使程序崩溃,它可能会做其他事情。

现在,说了,有可能查看地址,试图找出你的特定编译器如何在你的特定程序中布置变量,这是编译它的特定时间。为了在我的计算机上尝试这个,我不得不改变你的地址打印,因为我的指针是64位,你的程序试图使用整数打印它们,这是32位。但是在更改之后,结果是y在数组启动后被放置了56个字节,而不是编译器的60个字节。

然后我编译了优化打开,现在y在数组开始后(刚好在数组结束之后)的40个字节和之后的x 44个字节(也就是y之后)。 array [12]也在那之后(记住,数组中的最后一个元素编号为9!),但是通过将数组[12]更改为数组[11],我得到的程序将x打印为17。

但是,请记住,所有这些都是未定义的行为,并且您不应该编写依赖于编译器以任何特定顺序放置的变量的程序。

答案 6 :(得分:1)

无法保证堆栈中存在局部变量;编译器可能决定将一些变量放入寄存器中。假设对齐和寄存器问题得到解决,x无论如何都会在数组[12],因为array [9]是array []中的最后一个有效位置。

做这种事情充其量是不可移植的,而且总是一个坏主意。

答案 7 :(得分:1)

两件事

  1. [12]之所以有效 因为那个数组是最后一件事 堆栈。还有一个词 没有“上面”,所以你可以保持 写它。尝试切换阵列 声明,让我们说'x'和你 最有可能获得核心转储

  2. 您将内存地址打印为“signed ints”,因此它们可能是
    以负数出现。我会将'%d'更改为%u以查看正数。你会发现它们之间的差异是40而不是60.不确定你是如何获得60,可能是你错误地减去它们。

答案 8 :(得分:0)

这是直接内存访问的问题,以及为什么这么多语言现在不允许它。

您可以分配一个大小为10的数组,写入100的数组位置并且它可以工作,但是,您现在正在覆盖可能被其他程序或程序实际使用的内存,因此您可能已损坏实际应用。

您可以安全使用的唯一内存是您分配给程序的内存。您不知道x和y int在应用程序中的位置,因为它们可能位于内存中的任何位置,只是为变量预留了4个字节。

答案 9 :(得分:0)

问题已经得到解答,但我想指出,如果您需要这种行为,可以改用struct

struct {
    int array[10];
    int k;
    int x;
    int y;
} s;

s.k = s.y = 10;
s.x = 42;
s.array[12] = 17;
printf("array starts at %d\n", s.array);
printf("x has the value %d\n", s.x);
printf("x is stored in location %d\n", &s.x);

printf("y has the value %d\n", s.y);
printf("y is stored in location %d\n", &s.y);

答案 10 :(得分:0)

正如其他人所提到的,这是标准下未定义的行为,几乎可以做任何事情。另外如果我将地址的printf格式更改为更合适的“%p”,我在Mac OS X上得到的输出如下所示:

array starts at 0xbffff7d4
x has the value 42
x is stored in location 0xbffff7cc
y has the value 10
y is stored in location 0xbffff7d0

因此,在我的情况下,数组存储在比x或y更高的地址。为了看到写过数组的结尾,显然需要切换声明,以便在数组之前分配x和y变量。

当我这样做时,我明白了:

array starts at 0xbffff7cc
x has the value 42
x is stored in location 0xbffff7f4
y has the value 10
y is stored in location 0xbffff7f8

所以,现在他们的顺序是正确的,至少。但是,变量之间没有额外的填充,所以我需要将覆盖调整为数组[10],以获得这个:

array starts at 0xbffff7cc
x has the value 17
x is stored in location 0xbffff7f4
y has the value 10
y is stored in location 0xbffff7f8

成功,一种 - 我设法通过访问“数组”来覆盖“x”的值。事实证明,改变编译器设置中的几乎任何东西都会改变各种变量的地址。这就是为什么写堆栈粉碎安全漏洞就像它一样困难......

答案 11 :(得分:0)

int array[10];

    array[12] = 17;

非常有趣......

这是一个问题吗?