这不应该发出越界警告吗?
Shouldn't this give an out-of-bounds warning?
我认为这段代码应该警告越界数组访问:
int foo() {
int x[10] = {0};
int *p = &x[5];
return p[~0LLU];
}
我知道标准不需要越界警告,但编译器确实给出了它们。我问编译器在这里给出这样的警告是否正确。
为什么应该认为该代码格式良好?
我认为这段代码应该警告越界数组访问:
一个体面的编译器可以在非VLA数组上执行此操作时警告您(gcc没有,但clang可以:https://godbolt.org/z/lOvl5n)
对于此代码段:
int foo() {
int x[10] = {0};
return x[~0LLU]; // or x[40] to make it simpler, same thing
}
警告:
<source>:3:10: warning: array index -1 is past the end of the array (which contains 10 elements) [-Warray-bounds]
return x[~0LLU];
^ ~~~~~
编译器知道这是一个数组,知道大小,因此可以检查边界,如果一切都是文字的(非VLA数组和文字索引是先决条件)
在您的情况下,编译器"丢失"的是分配给指针(数组衰减为指针)
之后,编译器无法分辨数据的来源,因此它无法控制边界(即使在您的情况下,偏移量大得离谱/负数/其他什么)。专用的静态分析工具可能会发现问题。
C 语言对数组的边界检查没有要求。 这是使它变得快速的部分原因。 话虽如此,编译器可以并且确实在某些情况下执行检查。
例如,如果我在 gcc 中使用-O3
编译并将return p[~0LLU];
替换为return p[10];
,则会收到以下警告:
x1.c: In function ‘foo’:
x1.c:6:10: warning: ‘*((void *)&x+60)’ is used uninitialized in this function [-Wuninitialized]
return p[10];
如果我使用-10
作为索引,我会收到类似的警告:
gcc -g -O3 -Wall -Wextra -Warray-bounds -o x1 x1.c
x1.c: In function ‘foo’:
x1.c:6:10: warning: ‘*((void *)&x+-20)’ is used uninitialized in this function [-Wuninitialized]
return p[-100];
因此,它似乎可以警告数组索引的无效负值。
在您的情况下,对于此编译器来说,出于指针算术的目的,值~0LLU
似乎被转换为有符号值,并被视为 -1。
请注意,可以通过将其他初始化变量放在x
周围来欺骗此检查:
int foo() {
int y[10] = {0};
int x[10] = {0};
int z[10] = {0};
int *p = &x[5];
printf("&x=%p, &y=%p, &z=%pn", (void *)x, (void *)y, (void *)z);
return p[10] + y[0] + z[0];
}
即使p[10]
超出范围,此代码也不会生成警告。
因此,如果它想要执行越界检查以及如何执行,这取决于实现。
编辑:完全重写,用标准引号:
[dcl.array] [注意:除非为类声明了下标运算符 [] 的解释方式使
E1[E2]
与*((E1)+(E2))
相同[expr.add] 当在指针中添加或减去具有整型类型的表达式时,结果具有以下类型 的指针操作数。如果表达式
P
指向数组对象的元素x[i]
,x
n
元素, 表达式P + J
和J + P
(其中J
的值为j
)指向(可能假设的)元素x[i + j]
如果0 ≤ i + j ≤ n
;否则,行为是未定义的。
因此,p[~0LLU]
的解释与*(p + ~0LLU)
相同(根据 [dcl.array]),其中括号表达式指向元素x[5 + ~0LLU]
- 如果索引在有效范围内 - (根据 [expr.add])。如果索引不在范围内,则行为未定义。
不过,5 + ~0LLU
是否在指数的有效范围内?给定语言的整数转换规则,如果 5 的类型是不大于unsigned long long
的有符号类型,则显示的表达式似乎是明确定义的,在这种情况下,尖点元素将是x[4]
的。但是,标准没有在描述行为的表达式中明确定义i
和j
的类型。它应该被解释为一个纯粹的数学表达式,在这种情况下,结果将是一个无法用long long unsigned
表示的指数,并且肯定大于n
,因此是未定义的行为。
鉴于行为未定义的解释,编译器发出警告并不正确。无论如何,编译器不需要发出警告。
- 警告处理为错误这里有什么问题
- 使用动态分配的数组会导致代码分析发出虚假的C6386缓冲区溢出警告
- cppcheck在const std::string[]上引发警告
- GCC对可能有效的代码抛出init list生存期警告
- 如何在BST的这个简单递归实现中消除警告
- 关于std::move的使用,是否有编译警告
- g++ 在某个类成员未初始化时不发出警告
- 如何处理来自核心指南检查器的关于gsl::at的静态分析警告
- 使用typeid警告未使用的变量
- 示例C++项目编译中的警告
- 警告:在函数返回类型 [-Wignore 限定符] 时忽略类型限定符
- 如何修复编译器警告 C6386 和 C6385?
- 返回语句后的代码,没有警告
- 获取隐式转换溢出从无符号到已签名的警告
- 编译器警告:执行到达值返回函数的末尾而不返回值
- 在未链接的部分上生成警告
- 警告 C4552:">>":未使用表达式的结果
- 禁止显示由于常量为零而比较始终为假的警告
- C++ 警告:将新创建的 gsl::owner<> 分配给非所有者
- 这不应该发出越界警告吗?