【问题标题】:Array Size and Addresses in CC中的数组大小和地址
【发布时间】:2009-09-26 19:23:53
【问题描述】:

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

【问题讨论】:

  • 不要把时间浪费在探索未定义的行为上。并且不要将时间浪费在利用您的发现结果上。它永远不会有回报。真的。
  • 我同意你的观点,但这将在考试中进行测试......叹息......
  • 打印数字为无符号 %u。请参阅下面的解释
  • 所以你应该知道未定义的行为?可怜的你。另一方面,在这种情况下你不能完全错——一些奇怪平台上的一些奇怪的编译器会产生非常奇怪的结果:)
  • 无符号是不够的。在某些计算机上(例如我现在使用的计算机),地址是 64 位,而整数(包括无符号数)是 32 位。 unsigned long 通常足够好,但正确的方法是先转换为 void*,然后使用 %p 打印。

标签: c memory


【解决方案1】:

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

【讨论】:

  • 数组的地址呢?根据我的计算,为什么它们是 60 个单位而不是 48 个单位?
  • metashockwave:那是哪个“数组地址”?相隔 60 个单位的东西是什么?
  • y的存放位置和数组的开头
  • metashockwave:好的,所以 y 和数组的开头相距 60 个单位。和?编译器可以将变量放在任何它想要的地方。不能保证它们是一个接一个地存储在内存中,或者按照您定义它们的顺序存储。举个例子,编译器可能会生成一些程序执行计算所需的额外临时变量,并将这些额外变量放在正常变量之间!
  • 它们应该相隔 40 个字节而不是 48 个字节!。就像 x 和 y 相隔 4 个字节而不是 8 个!。假设 y 从位置 '0' 开始,那么您会期望 x 从位置 4 而不是 8 等开始。
【解决方案2】:

当我编译这段代码(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 个字节,位于最顶端。

&amp;y是y的起始地址,距离栈帧底部+4字节。 &amp;array[0]是数组的起始地址,距离栈底+12字节。

顺便说一句,您可能应该将您的地址格式化为“无符号” - %u。否则,大地址可能会显示为负数。

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

【讨论】:

  • 谢谢亚历克斯!我以不同的方式被教导:局部变量遵循开销意味着 x x x x y y y y k k k k array[10] array[10] array[10] array[10] array[9] array[9] array[9] array[9] array[8 ] ... . . .以此类推
  • 这些变量的布局不是由语言定义的,所以任何实现都是可能的。特别是,您的程序实际上并没有使用变量 k,我发现说服编译器将它包含在堆栈中非常棘手。
【解决方案3】:

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

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

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

【讨论】:

    【解决方案4】:

    &x 是变量“x”的地址,和你的数组没有关系,只是它恰好分配在附近的栈上。

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

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

    【讨论】:

      【解决方案5】:

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

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

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

      【讨论】:

      • C 甚至不保证栈的存在。
      【解决方案6】:

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

      现在,话虽如此,可以查看地址以尝试找出您的特定编译器如何在特定程序中布置变量,这个特定时间编译它。要在我的计算机上尝试此操作,我必须更改您的地址打印,因为我的指针是 64 位的,而您的程序尝试使用 32 位的整数来打印它们。但是在更改之后,事实证明 y 在数组开始后放置了 56 个字节,而不是像您的编译器那样放置 60 个字节。

      然后我在打开优化的情况下进行编译,现在 y 在数组开始后 40 个字节(就在数组末尾之后)和 x 在 44 个字节之后(即在 y 之后)。 array[12] 也在之后(记住,数组中的最后一个元素编号为 9!),但是通过将你的 array[12] 更改为 array[11],我让程序将 x 打印为 17。

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

      【讨论】:

        【解决方案7】:

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

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

        【讨论】:

          【解决方案8】:

          两件事

          1. a[12] 起作用的原因是 因为那个数组是最后一件事 堆栈。还有一句话 没有“高于”,所以你可以保留 写它。尝试切换阵列 声明让我们说'x'和你 最有可能获得核心转储

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

          【讨论】:

            【解决方案9】:

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

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

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

            【讨论】:

              【解决方案10】:

              问题已经得到解答,但我想指出,如果您需要这种行为,您可以改用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);
              

              【讨论】:

              • 我认为它不能保证预期的行为:结构并不总是被打包。在某些机器/编译器上,可能无法在不破坏对齐规则的情况下打包它。
              【解决方案11】:

              正如其他人所提到的,这是标准下未定义的行为,几乎可以做任何事情。此外,如果我将地址的 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
              

              所以,至少现在它们的顺序是正确的。不过,变量之间没有额外的填充,所以我需要调整对 array[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”的值。事实证明,改变编译器设置中的几乎任何东西都会改变各种变量的地址。这就是让编写堆栈粉碎安全漏洞变得如此困难的原因......

              【讨论】:

                【解决方案12】:
                int array[10];
                
                    array[12] = 17;
                

                非常有趣...

                这是一个问题?

                【讨论】:

                  猜你喜欢
                  • 2018-01-15
                  • 2013-02-13
                  • 1970-01-01
                  • 2011-05-03
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多