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;
}
$ 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
$ 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.
如果内联函数直接写在 main() 中,gcc 会发出数组越界警告,但为什么它不能用静态内联函数检测到相同的警告?
我不太了解输出汇编器,但在我看来,生成的循环确实是20次,所以在执行过程中,溢出是真实的。
inline
只是对编译器的(大部分已过时)建议,它不会 enforce 内联。碰巧的是,您确实会在 -O3
下收到警告,但在 -O2
下却没有收到警告。一些注意事项:
-O2
/-O3
选项确实会内联该函数(在 x86_64 gcc 13.3 上),但方式不同,并且 -O3
的一种方式恰好是呈现诊断消息的方式。inline
关键字不会改变生成的程序集,也不会改变内联的决定。__attribute__((always_inline))
对于您是否收到警告也没有影响。这里的最佳实践是不要依赖 gcc 发出此警告 - C 中的越界访问错误很少伴随着编译器警告。相反,您可以以类型安全的方式设计该函数:
static inline void myfunc(unsigned char buf[20])
或
static inline void myfunc(size_t size, unsigned char buf[size])
或者如果采取极端(非常安全但麻烦的API):
static inline void myfunc(unsigned char (*buf)[20])
如果您使用选项
-fsanitize=address
编译程序,您将在运行时进行数组边界检查。然而,Windows 上的 gcc 不支持此选项,但 clang 支持。有关安装说明,请参阅此答案:How to install MSYS2 clang with address and UB sanitizers