【问题标题】:Declared vs non-declared array in CC中声明的与未声明的数组
【发布时间】:2015-03-01 16:39:34
【问题描述】:

我遇到了这两个代码块:

#include <stdio.h>
int main() {
  int a[10], i;
  for (i = 1; i <= 10; i++) {
    scanf("%d", &a[i]);
    printf("%d\n", a[i]);
  }
  return 0
}

当我运行第一段代码时,代码运行良好,但有时会被覆盖,我没有得到预期的结果。

但是,当我运行第二段代码时,程序运行得非常好,没有错误。

#include <stdio.h>
int main() {
  int size;
  scanf("%d", &size);
  int a[size], i;
  for (i = 1; i <= size; i++) {
    scanf("%d", &a[i]);
    printf("%d\n", a[i]);
  }
  return 0
}

为什么程序在第二种情况下运行完美?即使在第二种情况下,下标也会超过声明的数组大小。

【问题讨论】:

  • 您使用的是哪个编译器/操作系统?
  • 顺便说一句,你的程序不应该编译,因为“return 0”错过了一个分号...
  • 未定义的行为是未定义的不良行为。你不能指望崩溃。

标签: c variable-length-array


【解决方案1】:

两个代码 sn-p 都有数组越界访问,因此您会看到未定义的行为

for(i=1;i<=size;i++)

如果您有大小为10 的数组,则有效索引为0-9

一旦我们说未定义的行为,那么任何事情都可能发生,甚至崩溃。当您说它正在工作时,您可能没有看到崩溃,但您仍然在访问一些您尚未分配的内存

【讨论】:

  • OP 知道错误并询问行为差异。
  • @MOehm 一旦我们说未定义的行为,那么幸运的是 OP 会看到事情可能工作正常
【解决方案2】:

问题中的代码(两种变体)显示'undefined behaviour',因此“一切皆有可能”。这包括“程序崩溃”、“程序表现得好像溢出是合法的”和“程序尽最大努力清除机器上的所有数据”。幸运的是,编译器编写者很少使用最后一个选项。 (另见维基百科文章中的“鼻恶魔”。)

问题中的代码(两种变体)已损坏。直到 for 循环变为惯用的:

for (i = 0; i < 10; i++)

程序只是被破坏了,调用了未定义的行为,当它们运行时任何事情都可能发生。

如果您愿意,您可以分析为这两个错误程序生成的程序集(但最好不要)。我对行为差异的最佳猜测是,具有恒定长度数组 (CLA) 的版本对数组和索引变量的布局与具有可变长度数组 (VLA) 的版本不同,因此会溢出 VLA 中的数组版本会覆盖 CLA 版本中溢出数组的不同数据。

但是详细调查发生的事情没有什么意义。该行为是未定义的,并且知道在一个平台上使用一组编译标志的一个源文件和一个编译器的一个版本会发生什么,不会使代码正确、可靠、可移植或任何其他有用的东西。添加第二个数组可以改变事情;更改编译器版本、使用不同的编译器、迁移到新平台或更改编译的优化级别——任何这些都可能改变所发生的事情,而且你没有人可以责备自己,因为代码被破坏了。问题出在代码上,而不是编译器如何处理它。

【讨论】:

  • :这是否意味着编译器在常量长度数组和可变长度数组的情况下表现不同?我想知道的是,为什么当我在代码本身中初始化数组大小时代码会表现出特定的行为,为什么当我从用户输入数组大小时它工作得非常正常,即使我的 for 循环从1而不是0?我知道用 1 开始一个数组的 for 循环是一个错误,编译器没有显示,但我的问题是为什么它在我的问题中提到的 2 种情况下表现不同?
  • 我建议你看一下生成的汇编器。优化器可以相同地处理此代码,但对于 VLA(标准首字母缩写词)与 CLA(非标准首字母缩写词)的一般情况,很可能存在差异。如果单个函数中有多个 VLA,这些将更加明显,其大小由函数的参数确定。一些数组的起始地址不能在编译时相对于堆栈指针固定,但可能会根据数组的大小而变化。所以,生成的汇编器会有所不同。
  • 考虑:void matrix_add_sub_print(int n, int m, int matrixA[m][n], int matrixB[m][n]) { int matrixS[m][n]; int matrixD[m][n]; for (int i = 0; i &lt; m; i++) for (int j = 0; j &lt; n; j++) { matrixS[i][j] = matrixA[i][j] + matrixB[i][j]; matrixD[i][j] = matrixA[i][j] - matrixB[i][j]; } print_matrix(n, m, matrixS); print_matrix(n, m, matrixD); }(警告:未经测试的代码)。如果调用是matrix_add_sub_print(3, 3, matrix1, matrix2); vs matrix_add_sub_print(100, 100, matrix3, matrix4);,两个本地矩阵的位置会不同。
【解决方案3】:

@Ashalynd:操作系统是 Windows 8.1,我正在使用 CodeBlocks。漏掉的分号是打字错误。 @JonathanLeffler:这是否意味着编译器在恒定长度数组和可变长度数组的情况下表现不同? 我想知道的是,为什么当我在代码本身中初始化数组大小时代码会表现出特定的行为,为什么当我从用户输入数组大小时它工作得非常正常,即使我的 for 循环从1而不是0? 我知道用 1 开始一个数组的 for 循环是一个错误,编译器没有显示,但我的问题是为什么它在我的问题中上面提到的 2 种情况下表现不同?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-02-09
    • 1970-01-01
    • 1970-01-01
    • 2012-07-14
    • 2021-07-06
    • 2012-05-09
    • 2020-10-24
    • 2017-04-07
    相关资源
    最近更新 更多