可变长度数组的代码顺序
code order with variable length array
在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
以及 returns 给出了一个不同的段错误输入 9 9
的值大于 5。因此,您是否需要确保在使用可变长度数组声明它们之前获取这些值,还是这里发生了其他事情?
第二个应该会产生错误,因为如果 n 和 m 是局部变量,它们会使用相当多的随机值进行初始化。如果它们是全局的,它们的值为 0。
当第二个成功时,纯属偶然。它曾经有效的事实证明,谢天谢地,编译器还不能 make demons fly out of your nose.
声明一个变量并不一定要初始化它。在这种情况下,int n, m;
将 n
和 m
都保留为 undefined 值,并且尝试访问这些值是未定义的行为。如果内存中那些指向 的原始二进制数据发生 被解释为大于为 n
和 m
输入的值的值——这非常, 远不能保证——那么你的代码就可以工作;如果没有,它不会。你的编译器也可能造成了这个段错误,或者让它融化了你的 CPU;这是未定义的行为,所以任何事情都有可能发生。
例如,假设编译器专用于 n
的内存区域恰好包含数字 10589231
,而 m
得到 14
。如果您随后输入了 12 的 n
和 6 的 m
,那么您就太棒了——数组恰好足够大。另一方面,如果 n
得到 4 而 m
得到 2,那么您的代码将查看数组的末尾,并且您将得到未定义的行为——甚至可能不会中断,因为根据您的 compiler/the C 标准,您的程序和有效整数完全有可能存储在数组末尾之后的四字节段中的位。此外,n
和 m
有可能以负值结束,这会导致……奇怪的事情。应该是吧。
当然,这完全是胡说八道和推测,具体取决于编译器、OS、一天中的时间和月相,1,你可以'不要依赖任何恰好被初始化为正确数字的数字。
另一方面,对于第一个,您通过 scanf
分配值,因此(假设它没有错误)(并且输入的数字不是负数)(或零) 您将拥有有效的索引,因为数组保证足够大,因为变量已正确初始化。
需要说明的是,即使在某些情况下需要对变量进行零初始化,也不意味着您应该依赖该行为。你应该总是显式地给变量一个默认值,或者在它们声明后尽快初始化它们(在使用像 scanf
这样的东西的情况下)。这使您的代码更清晰,并防止人们怀疑您是否依赖这种类型的 UB。
1:来源:Ryan Bemrose,在聊天中
int X[n][m];
表示声明一个数组,其维度为n
和m
当前拥有的值。 C 代码不展望未来;语句和声明按照它们遇到的顺序执行。
在你的第二个代码中你没有给出 n
或 m
值,所以这是 未定义的行为 这意味着任何事情都可能发生。
这是顺序执行的另一个例子:
int x = 5;
printf("%d\n", x);
x = 7;
这将打印 5
,而不是 7
。
在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
以及 returns 给出了一个不同的段错误输入 9 9
的值大于 5。因此,您是否需要确保在使用可变长度数组声明它们之前获取这些值,还是这里发生了其他事情?
第二个应该会产生错误,因为如果 n 和 m 是局部变量,它们会使用相当多的随机值进行初始化。如果它们是全局的,它们的值为 0。
当第二个成功时,纯属偶然。它曾经有效的事实证明,谢天谢地,编译器还不能 make demons fly out of your nose.
声明一个变量并不一定要初始化它。在这种情况下,int n, m;
将 n
和 m
都保留为 undefined 值,并且尝试访问这些值是未定义的行为。如果内存中那些指向 的原始二进制数据发生 被解释为大于为 n
和 m
输入的值的值——这非常, 远不能保证——那么你的代码就可以工作;如果没有,它不会。你的编译器也可能造成了这个段错误,或者让它融化了你的 CPU;这是未定义的行为,所以任何事情都有可能发生。
例如,假设编译器专用于 n
的内存区域恰好包含数字 10589231
,而 m
得到 14
。如果您随后输入了 12 的 n
和 6 的 m
,那么您就太棒了——数组恰好足够大。另一方面,如果 n
得到 4 而 m
得到 2,那么您的代码将查看数组的末尾,并且您将得到未定义的行为——甚至可能不会中断,因为根据您的 compiler/the C 标准,您的程序和有效整数完全有可能存储在数组末尾之后的四字节段中的位。此外,n
和 m
有可能以负值结束,这会导致……奇怪的事情。应该是吧。
当然,这完全是胡说八道和推测,具体取决于编译器、OS、一天中的时间和月相,1,你可以'不要依赖任何恰好被初始化为正确数字的数字。
另一方面,对于第一个,您通过 scanf
分配值,因此(假设它没有错误)(并且输入的数字不是负数)(或零) 您将拥有有效的索引,因为数组保证足够大,因为变量已正确初始化。
需要说明的是,即使在某些情况下需要对变量进行零初始化,也不意味着您应该依赖该行为。你应该总是显式地给变量一个默认值,或者在它们声明后尽快初始化它们(在使用像 scanf
这样的东西的情况下)。这使您的代码更清晰,并防止人们怀疑您是否依赖这种类型的 UB。
1:来源:Ryan Bemrose,在聊天中
int X[n][m];
表示声明一个数组,其维度为n
和m
当前拥有的值。 C 代码不展望未来;语句和声明按照它们遇到的顺序执行。
在你的第二个代码中你没有给出 n
或 m
值,所以这是 未定义的行为 这意味着任何事情都可能发生。
这是顺序执行的另一个例子:
int x = 5;
printf("%d\n", x);
x = 7;
这将打印 5
,而不是 7
。