当我编译下面的代码时,它显示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);
}
答案 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个字节,位于最顶层。
&y
是y
的起始地址,它是堆栈帧底部的+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)
两件事
[12]之所以有效 因为那个数组是最后一件事 堆栈。还有一个词 没有“上面”,所以你可以保持 写它。尝试切换阵列 声明,让我们说'x'和你 最有可能获得核心转储
您将内存地址打印为“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;
非常有趣......
这是一个问题吗?