gcc 未检测到内联函数的数组越界

poz*_*gno 0 c gcc gcc-warning

extern void myprint(unsigned char *);

static inline void
myfunc(unsigned char *buf)
{
    for (unsigned int i = 0; i < 20; i++) {
        buf[i] = i;
    }
}

int
main(void)
{
    unsigned char buf[10];
    myfunc(buf);
    myprint(buf);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)
$ gcc.exe -O2 -pedantic -Wextra -Wall -Wstringop-overflow -Wuninitialized -Wunused -Warray-bounds=2 -Wformat-overflow -Wstringop-overread -g  -c C:\temp\cb\main.c -o build\mingw\obj\main.o
$ gcc.exe  -o build\mingw\test.exe build\mingw\obj\main.o build\mingw\obj\mod.o  -O2
Run Code Online (Sandbox Code Playgroud)
$ gcc --version
gcc (i686-posix-sjlj-rev1, Built by MinGW-W64 project) 11.2.0
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Run Code Online (Sandbox Code Playgroud)

如果内联函数直接写在 main() 内部,gcc 会发出数组越界警告,但为什么它不能用静态内联函数检测到相同的警告呢?

我不太了解输出汇编器,但在我看来,生成的循环确实是 20 次,因此在执行过程中溢出是真实的。

编辑于 2023 年 12 月 1 日 09:13

阅读一些回复后,我尝试更改 myfunc() API,但使用 -O2 时,gcc 不会发出警告。

#include <stddef.h>

extern void myprint(unsigned char *);

static inline void
myfunc(unsigned char *buf, size_t size)
{
    for (unsigned int i = 0; i < size + 1; i++) {
        buf[i] = i;
    }
}

int
main(void)
{
    unsigned char buf[10];
    myfunc(buf, 10);
    myprint(buf);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

Lun*_*din 5

inline只是对编译器的建议(大部分已过时),它不强制内联。碰巧的是,您确实会在 下收到警告,-O3但不是在 下-O2。一些注意事项:

  • -O2/选项确实-O3会内联该函数(在 x86_64 gcc 13.3 上),但方式不同,其中一种方式-O3恰好是呈现诊断消息的方式。
  • 在这种情况下,存在/不存在inline关键字不会改变生成的程序集,也不会改变内联的决定。
  • __attribute__((always_inline))对您是否收到警告也没有影响。

这里的最佳实践是不要依赖 gcc 发出此警告 - C 中的越界访问错误很少伴随着编译器警告。相反,您可以以类型安全的方式设计该函数:

static inline void myfunc(unsigned char buf[20])
Run Code Online (Sandbox Code Playgroud)

或者

static inline void myfunc(size_t size, unsigned char buf[size])
Run Code Online (Sandbox Code Playgroud)

或者如果采取极端的话(非常安全但API很麻烦):

static inline void myfunc(unsigned char (*buf)[20])
Run Code Online (Sandbox Code Playgroud)