繁体   English   中英

C中的数组大小和地址

[英]Array Size and Addresses in C

当我编译下面的代码时,它显示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);
 }

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

当我编译这段代码(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 为了让它像这样工作,我不得不编译优化 - 你的里程可能会有所不同:)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

两件事情

  1. [12]之所以有效,是因为该数组是堆栈中的最后一个。 另一个词没有“上面”,所以你可以继续写它。 尝试切换数组声明,让我们说'x',你很可能得到一个核心转储

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

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

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

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

问题已经回答了,但我想指出,如果你需要这种行为,你可以使用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);

正如其他人所提到的,这是标准下未定义的行为,几乎可以做任何事情。 另外如果我将地址的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”的值。 事实证明,改变编译器设置中的几乎任何东西都会改变各种变量的地址。 这就是为什么写堆栈粉碎安全漏洞就像它一样困难......

int array[10];

    array[12] = 17;

非常有趣...

这是一个问题?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM