在C99中,这两者之间有很大不同吗?:
int main() {
int n , m;
scanf("%d %d", &n, &m);
int X[n][m];
X[n-1][m-1] = 5;
printf("%d", X[n-1][m-1]);
}
和
int main(int argc, char *argv[]) {
int n , m;
int X[n][m];
scanf("%d %d", &n, &m);
X[n-1][m-1] = 5;
printf("%d", X[n-1][m-1]);
}
第一个似乎总能工作,而第二个似乎适用于大多数输入,但为输入5 5
和6 6
提供了段错误,并为输入返回不同于5的值9 9
。那么你需要确保在使用可变长度数组声明它们之前得到这些值,或者这里还有其他东西吗?
答案 0 :(得分:4)
当第二个工作时,这是纯粹的机会。它曾经有效的事实证明,幸运的是,编译器还不能make demons fly out of your nose。
声明变量不一定要初始化它。在这种情况下,int n, m;
会为n
和m
留下未定义的值,并且尝试访问这些值是未定义的行为。如果内存中的原始二进制数据指向发生,则会将其解释为大于为n
和m
输入的值的值 - 这是非常非常远的从保证 - 然后你的代码将工作;如果没有,它不会。您的编译器也可以使这个段错误,或者使它融化您的CPU;它是未定义的行为,所以任何事情都可能发生。
例如,假设编译器专用于n
的内存区域恰好包含数字10589231
,而m
包含14
。如果你输入了一个12的n
和一个6的m
,你就是金色的 - 阵列恰好足够大了。另一方面,如果n
得到4并且m
得到2,那么你的代码将看起来超过数组的末尾,你会得到未定义的行为 - 甚至可能不会破坏,因为根据编译器/ C标准,数组结束后存储在四字节段中的位完全有可能被程序访问,并且有效整数。此外,n
和m
可能会以负值结束,从而导致......奇怪的东西。可能。
当然,根据编译器,操作系统,一天中的时间和月亮的阶段, 1 ,所有这些都是粗暴和猜测,你不能依赖任何正在发生的数字来初始化到正确的。
另一方面,对于第一个,你通过scanf
分配值,所以(假设它没有错误)(并且输入的数字不是负数)(或为零)你'将拥有有效的索引,因为数组保证足够大,因为变量已正确初始化。
需要明确的是,即使在某些情况下要求变量零初始化并不意味着您应该依赖该行为。您应该始终明确地为变量赋予默认值,或者在声明之后尽快初始化它们(在使用scanf
之类的情况下)。这使您的代码更加清晰,并且可以防止人们想知道您是否依赖这种类型的UB。
1:来源:Ryan Bemrose,聊天
答案 1 :(得分:4)
n
表示声明一个数组,其维度是m
和n
当前具有的值。 C代码不会考虑未来;语句和声明按它们遇到的顺序执行。
在您的第二个代码中,您没有提供m
或int x = 5;
printf("%d\n", x);
x = 7;
值,因此这是未定义的行为,这意味着可能发生任何事情。
这是顺序执行的另一个例子:
5
这将打印7
,而不是{{1}}。
答案 2 :(得分:-1)
第二个应该产生错误,因为如果它们是局部变量,则n和m用相当多的随机值初始化。如果它们是全球性的,它们将具有价值0。