玩一些C代码在C99和Rust之间做一些比较,我写了以下内容,
typedef enum {
NICKLE, DIME, QUARTER, DOLLAR,
} Denom;
static const int cents[] = {
[NICKLE] = 5,
[DIME] = 10,
[QUARTER] = 25,
[DOLLAR] = 100,
};
int main () {
printf( "Result %d\n", cents[DIME] );
}
这可以按预期运行,现在我想显示访问权限以及访问权限
typedef enum {
NICKLE, DIME, QUARTER, DOLLAR, ZOD = 20394
} Denom;
我期待着
printf( "Result %d\n", cents[ZOD] );
显示访问权限为cents + ZOD
。但事实并非如此。
mov eax, 0
mov esi, eax
lea rdi, str.Result__d ; 0x5570bf1ed020 ; "Result %d\n"
mov eax, 0
call sym.imp.printf ; int printf(const char *format)
为什么将esi
设置为0
,为什么打印
Result 0
-Wall -Wextra -Wpedantic
也没有错误。 GCC是否提供管理此行为的标志?它是否知道这超出范围?如果是,它是否也不会发出警告(或可以发出警告)?
答案 0 :(得分:1)
是的,GCC可以在特定情况下对此发出警告(请参阅@Acorn的答案)。
有一个运行时未定义行为检查器-fsanitize=undefined
,如果您执行某些类型的未定义行为(例如,访问过去的数组边界),则会出现错误。
由于这是未定义的行为,因此编译器可以自由执行任何所需的操作。这意味着它可以将esi
设置为零,密码的十六进制表示或其他任何形式。
答案 1 :(得分:1)
要补充@ JL2210的答案,请注意,其他编译器也会根据问题中给出的源发出警告。
例如Clang:
<source>:15:28: warning: array index 20394 is past the end of the array (which contains 4 elements) [-Warray-bounds]
printf( "Result %d\n", cents[ZOD] );
^ ~~~
<source>:7:1: note: array 'cents' declared here
static const int cents[] = {
^
以及icc:
<source>(15): warning #175: subscript out of range
printf( "Result %d\n", cents[ZOD] );
对于GCC,您拥有-Warray-bounds
的{{1}}。特别是,您需要-Wall -O2
(这种优化方法可以删除不需要的不必要的范围检查,例如数组绑定检查)。
但是,由于某些原因,GCC仅针对非-ftree-vrp
非const
数组给出警告。如果您尝试:
static
您会看到它的警告:
#include <stdio.h>
typedef enum {
NICKLE, DIME, QUARTER, DOLLAR, ZOD = 20394
} Denom;
int cents[] = {
[NICKLE] = 5,
[DIME] = 10,
[QUARTER] = 25,
[DOLLAR] = 100,
};
int main () {
printf( "Result %d\n", cents[ZOD] );
}
我至少可以从GCC 4.4.7开始看到警告!
答案 2 :(得分:0)
C语言定义不要求对数组访问进行任何类型的边界检查-编译器或运行时环境都不需要以任何方式处理越界访问。行为是 undefined 。
如果在翻译过程中可以检测到越界访问,并且随着时间的推移,越来越多的人这样做(如其他发布者所说,gcc和clang可以,但不是),则单个编译器可以发出诊断信息默认情况下)。
如果直到运行时都无法检测到它-好的,C没有任何结构化的异常处理机制,因此即使运行时环境发出信号,也没有好的方法来处理该问题。