我不知道为什么这段代码符合:
int array[100];
array[-50] = 100; // Crash!!
...编译器仍然可以正确编译,没有编译错误和警告。
那么为什么它会编译呢?
我不知道为什么这段代码符合:
int array[100];
array[-50] = 100; // Crash!!
...编译器仍然可以正确编译,没有编译错误和警告。
那么为什么它会编译呢?
array[-50] = 100;
其实这里的意思:
*(array - 50) = 100;
考虑以下代码:
int array[100];
int *b = &(a[50]);
b[-20] = 5;
此代码有效且不会崩溃。编译器无法知道代码是否会崩溃以及程序员想要对数组做什么。所以它不会抱怨。
最后,请注意,在查找代码中的错误时不应依赖编译器警告。编译器不会找到您的大部分错误,他们几乎不会尝试为您提供一些提示以简化错误修复过程(有时他们甚至可能会出错并指出,有效代码有错误)。此外,该标准实际上从不要求编译器发出警告,因此这些只是编译器实现者的善意行为。
它编译是因为表达式array[-50]
被转换为等价的
*(&array[0] + (-50))
根据通常的指针算术规则,这是另一种说法,即“取内存地址&array[0]
并将其添加 -50 次sizeof(array[0])
,然后将结果内存地址的内容及其后面的内容解释为”。int
这是一个完全有效的表达式,-50
它实际上可能是任何整数(当然它不需要是编译时常量)。
现在可以肯定的是,因为这里-50
是一个编译时常量,并且由于访问数组的第 50 个元素几乎总是一个错误,所以编译器可以(也许应该)为此产生警告。
但是,我们还应该考虑到检测这种特定条件(静态索引到具有明显无效索引的数组)是您不希望在实际代码中看到的东西。因此,编译器团队的资源可能会更好地用于其他事情。
if (answer = 42)
将此与您希望在实际代码中看到的其他构造进行对比(如果只是因为它很容易打错字)并且难以调试(眼睛可以很容易地阅读=
为==
,而这会-50
立即突出)。在这些情况下,编译器警告会更有效率。
真正的编译器可以(注意:需要将编译器切换到 clang 3.2,gcc 对用户不友好)
Compilation finished with warnings:
source.cpp:3:4: warning: array index -50 is before the beginning of the array [-Warray-bounds]
array[-50] = 100;
^ ~~~
source.cpp:2:4: note: array 'array' declared here
int array[100];
^
1 warning generated.
如果你有一个较小的 (*) 编译器,你可能必须手动设置警告。
(*) 即用户友好性较低
编译器不需要在编译时捕获所有潜在问题。C 标准允许在运行时出现未定义的行为(这是执行该程序时发生的情况)。您可以将其视为不捕获此类错误的合法借口。
不过,有些编译器和静态程序分析器可以捕捉到这样的微不足道的错误。
括号内的数字只是一个索引。它告诉您要在内存中执行多少步才能找到您请求的数字。 array[2]
表示从数组的开头开始,向前跳转两次。
你只是告诉它向后跳 50 次,这是一个有效的声明。但是,我无法想象这样做有充分的理由......