访问 int (*foo)[3] 的索引 4 时没有警告

No warnings when accessing index 4 of int (*foo)[3]

令我惊讶的是,在下面的代码中 gcc 没有警告我有关索引的信息。这是什么原因?

int foo[3] = {1,2,3};
int (*bar)[3];

int main(void) {
    bar = &foo;
    foo[42] = 42;
    (*bar)[42] = 42;
    return 0;
}

我只得到 foo 的错误,bar 没有。为什么?

$ gcc -std=c99 -Wall -O2 -Wpedantic -Wextra test.c
test.c: In function ‘main’:
test.c:6:8: warning: array subscript is above array bounds [-Warray-bounds]
     foo[42] = NOPE;
        ^

备注

我本可以编写 bar = foo 并将 bar 声明为 int* bar,但我没有,我也不知道为什么...一种解决方案比另一种更好,这是另一个有趣的问题。

指针不携带大小信息,因此编译器无法检测越界访问。

GCC 似乎不保留和使用指向数组的指针的大小信息,即使它会在 bar=&foo 处抱怨不兼容的类型,如果您更改 bar:

int foo[3] = {1,2,3};
int (*bar)[4];

int main(void) {
    bar = &foo;
}
39893471.c: In function ‘main’:
39893471.c:5:9: warning: assignment from incompatible pointer type [-Wincompatible-pointer-types]
     bar = &foo;
         ^

我在 GCC 版本 4.8 和 6.1 上确认了此行为。

N.B。 -O2 参数是 -Warray-bounds 工作所必需的;这让我愣了一会儿!

经过一些实验,我意识到只有当两个数组索引相同时才会显示两个警告,即 foo 和 bar 均为 42。 如果将索引更改为不同的值,它似乎会显示所有警告。下面的 foo1 和 bar1 的定义与 foo 和 bar 完全相同。

bar = &foo;
bar1 = &foo1;
foo[10] = NOPE;
(*bar)[10] = NOPE;
(*bar1)[11] = NOPE;
 foo1[12] = NOPE;

由于 bar 指向 foo,并且 gcc 已经对同一地址显示了警告,因此如果索引相同,则不会为 bar 显示相同的警告。反之亦然。在这里,警告显示的是 foo、bar1 和 foo1,而不是 bar。

希望这是一个合理的解释。

如果您评论 foo 访问权限,它会在 bar 访问权限上显示警告,所以这似乎只是 gcc 没有向您显示两次相同警告的情况。

它们是否应该被归类为相同的警告是另一回事 - 看看一方面有两个不同类型的变量,但另一方面它们为相同的内存起了别名。但这对我来说似乎很合理。最坏的情况是你进行额外的编译,直到你修复它们。

int foo[3] = {1,2,3};
int (*bar)[3];

int main()
{
    bar = &foo;
    // foo[42] = 42;
    (*bar)[42] = 42; // warning
    return 0;
}

20 : warning: array subscript is above array bounds [-Warray-bounds]
(*bar)[42] = 42;
~~~~~~~~~^