【问题标题】:Overfilling arrays in C [duplicate]C中的过度填充数组[重复]
【发布时间】:2020-01-12 07:59:13
【问题描述】:

如果我在 C 中(在 Windows 上)声明 2 个数组 arr1 和 arr2,并用整数 0 到 15 填充 arr1,则部分溢出将进入 arr2,但不是全部。为什么?

  int arr1[10];
  int arr2[10];
  int arr3[10];
  int i;

  for (i = 0 ; i < 10 ; i++)
    {
     arr1[i] = 100 + i;
     arr3[i] = 300 + i;
    }

  for (i = 0 ; i < 15 ; i++)
    {
     arr2[i] = 200 + i;
    }

  printf ("arr1: ");
  for (i = 0 ; i < 10 ; i++)
    printf ("%d, ", arr1[i]);
  printf ("\n");

  printf ("arr2: ");
  for (i = 0 ; i < 10 ; i++)
    printf ("%d, ", arr2[i]);
  printf ("\n");

  printf ("arr3: ");
  for (i = 0 ; i < 10 ; i++)
    printf ("%d, ", arr3[i]);
  printf ("\n");

输出:

arr1: 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 
arr2: 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 
arr3: 212, 213, 214, 303, 304, 305, 306, 307, 308, 309, 

预期:

arr1: 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 
arr2: 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 
arr3: 210, 211, 212, 213, 214, 305, 306, 307, 308, 309,

【问题讨论】:

  • 因为未定义的行为没有任何保证的结果。它可以做你期望它做的事情,它可以使程序崩溃,或者完全不同的事情。它甚至不必以任何形式保持一致。干脆别干了,没用也不实用,而且当它停止工作并导致内存损坏等难以调试的问题时,它只会让人头疼。
  • 当你覆盖数组时,你只是写入数组开头的内存地址加上 i * int 的大小。你怎么知道其他数组的分配位置?为什么你会假设它们只是在内存中一个接一个地堆叠?
  • 这将是因为编译器将数组对齐到 16 字节边界,= 4 个整数。如果您使用 4 的倍数而不是 10,那么它们很可能会相互流入。但是这种行为可能因操作系统和编译器而异,因此不要依赖它(也不要完全停止)。如果你需要 arr1,2,3 是连续的,那么分配一个内存块并分配 arr2 和 arr3 指向你想要的地方。
  • 未定义的行为不是严格随机的,但也可能是随机的。问你为什么没有得到你“期望”的行为就像在问,“昨晚我穿过马路,路牌上写着不要走路。我原以为会从警察那里得到一张乱穿马路的罚单。结果我被卡车。为什么?”

标签: c


【解决方案1】:

如果超出数组的大小,C 只会导致未定义的行为。

未定义行为的定义:

C11(ISO/IEC 9899:201x) §3.4.3

1 undefined behavior

behavior, upon use of a non portable or erroneous program construct or of erroneous data, for which this International Standard imposes no requirements

2 NOTE Possible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).

3 EXAMPLE An example of undefined behavior is the behavior on integer overflow.

C11 §J.2 中还有一个未定义行为列表 未定义行为

这里你的数组刚刚被打包在内存中。

如果我在我的计算机上使用另一个 linux 发行版和另一个编译器运行这个程序,我可能不会得到与你相同的结果,因为这是未定义的行为。

记住要避免未定义的行为

【讨论】:

  • 全局变量在内存中的排列方式有规律吗?
  • @A.Franzen 不,变量只是存储在您有足够空间的内存中,这里数组可能位于 3 个非常不同的地方,我们不会看到这种行为,而是另一个
【解决方案2】:

这可能是因为您的arr3 恰好在内存中arr2 之后的2 个位置,所以它在这些位置写入了210 和211(可能为空或被另一个变量程序使用)并开始覆盖arr3 212 在它的第一个单元格中。

但是,正如其他人已经说过的,这种行为是完全随机的,应该避免(你永远不知道arr3 是否会在arr2 之后被声明为内存中的 1 个或 20 个或 10000 个位置)

【讨论】:

  • 都是正确的,但我仍然想知道为什么编译器会这样做。我没有看到造成差距的明显原因。
  • @A.Franzen 在我们谈论未定义行为时,您不应该试图去想明显的原因,事情就是按照它们发生的方式发生,不要试图去理解它
  • 我不确定,可能是完全随机的
  • 我认为@A.Franzen 正在询问这个stackoverflow.com/q/19101449/10403061
  • 是的,确实,感谢您的链接。
【解决方案3】:

这里最有可能的是程序的其他部分在 arr2 之后直接在内存中分配了 8 个(或 16 个,取决于架构)字节,因此 arr3 最终被分配在这些字节之后。因此,当您写入 210 和 211 时,它们实际上在将其余部分写入 arr3 之前覆盖了其他内存。

【讨论】:

  • 不,我认为这不对。全局变量在对象文件中有它们的空间,它们不是动态分配的。出于某种原因,编译器在两个数组之间放了一些东西。
猜你喜欢
  • 2018-05-07
  • 2014-12-23
  • 2013-02-01
  • 2017-03-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-24
  • 2016-05-09
相关资源
最近更新 更多