这是我的代码。它应该首先扫描两个数字m和n,然后扫描一个数组x[n]。第二行扫描的数字数应该是n.但是它不能正常工作。
#include <stdio.h>
int main()
{
int m = 0;
int n = 0;
int x[n];
scanf("%d", &m);
scanf("%d", &n);
for(int i = 0 ;i < n;i++)
{
scanf("%d", &x[i]);
}
}
它只扫描一个数字,然后跳出循环。我们应该有(i)s从0到n-1;
int n = 0;
int x[n];
试图声明一个大小为0的可变长度数组,这已经是未定义的行为。
即使这是可能的(有一个零元素空间的VLA),
scanf("%d", &x[i]);
…也会是未定义行为。
让我试着解释考虑x86_64
架构和gcc7.5x
编译器的代码行为。
这个答案和这个问题的答案提供了x86
在不同架构上堆栈增长的方向,它是向下的,这意味着堆栈将从高内存增长到低内存地址(0xFF—>0 x00)
如果我们稍微修改一下代码,在运行时打印变量的地址和值,如下所示:
#include <stdio.h>
int main()
{
int m = 0;
int n = 0;
int x[n];
printf("Size of int: %dn", sizeof(int));
printf("Address ofnm: %pnn: %pnx: %pn", &m, &n, &x);
printf("Enter a value of m: ");
scanf("%d", &m);
printf("Enter a value of n: ");
scanf("%d", &n);
for(int i = 0; i < n; i++)
{
scanf("%d", &x[i]);
printf("Addr: %pn", &x[i]);
}
for(int j = 0; j < n; j++)
{
printf("x[%d]: %dn", x[j]);
}
printf("m: %d n: %dn", m, n);
}
在x86
上编译并运行上述代码后,我们可以注意到局部变量的地址按降序分配,如下面的代码执行会话所示:
Address of
m: 0x7ffdfb2b1704
n: 0x7ffdfb2b1700
x: 0x7ffdfb2b16f0
如果你注意到n
和x
的地址的差异,它们是16-Bytes
分开的,理想情况下它们应该是4-bytes
分开的。
但是由于int x[n]
被声明为0个整数的数组,理想情况下它应该占用4字节的内存,即使底层编译器和架构机制将其视为4个整数的数组,它被转换为4 integers * sizof(int) = 16-bytes
查看代码的进一步执行输出,我们可以看到地址0x7ffdfb2b1700
处的n
值随着代码的进一步执行而被覆盖。
请注意,对于
n < 5
的值,代码将正常工作,因为对于n = 4
的值,循环将在n = 3
处终止,在这种情况下,n
的值将不会被覆盖,因为循环迭代为(n * sizeof(int)) ==> 3 * 4 = 12-bytes
:
Enter a value of m: 1
Enter a value of n: 10
1
Addr: 0x7ffdfb2b16f0
2
Addr: 0x7ffdfb2b16f4
3
Addr: 0x7ffdfb2b16f8
4
Addr: 0x7ffdfb2b16fc
5
Addr: 0x7ffdfb2b1700 <--- address of variable `n` getting overwritten with new value
x[1]: 0
x[2]: 1
x[3]: 2
x[4]: 3
x[5]: 4
m: 1 n: 5
这就是为什么@DevSolar回答说这将导致未定义行为的原因。
我希望我能提供一些澄清。