今天来分享一个很久之前的bug。
把项目中的代码使用另一种方式来表述,大致是这样的:
#include <iostream>
#include <cstring>
#include <cstdlib>
void fun(std::size_t size){
char buffer[size];
std::memset(buffer, 0, size);
// 防止优化掉
std::cout << buffer << std::endl;
}
int main(int argc, char* argv[]){
if (argc < 2) {
std::cerr << "Usage: ./a.out <size>\n";
return 1;
}
std::size_t size = std::stoul(argv[1]);
fun(size);
return 0;
}
这段代码一直运行的很好,直至某一天,传入了一个合理的值(超过默认栈大小),之后程序开始崩溃,出现 segmentation fault。原因是:代码试图在栈上分配过大的内存。
当然了,修复方式也很简单,无非是以下几种:
-
• std::vector v(size)
-
• std::string s(size, 0)
-
• std::unique_ptr<char[]>(new char[size])
-
• 等等
我当时的疑惑是:这段代码一开始是怎么成功编译的?
根据C标准,栈上分配的对象(局部数组)的大小必须在编译期已知。但示例中`char buffer[size];`明显不符合标准,后面查了相关资料,**这种写法属于VLA(边长数组),这是 C99 的特性,不属于 C**。
那么问题来了:C++ 不支持变长数组但 GCC 和 Clang 却能编译这段代码,这是因为GCC 与 Clang 默认启用了 C99 扩展。
可以采用以下方式来避免此种错误:我们可以使用-Werror=vla来避免,即编译命令中加上即可,这样报错如下:
<source>:10:10: error: variable length array 'buffer' is used [-Werror=vla]
10 | char buffer[size];
| ^~~~~~
当然了,也有更严格的限制:-pedantic。它告诉编译器严格按照 C++ 标准,不允许任何扩展。
输出如下:
<source>:10:10: warning: ISO C++ forbids variable length array 'buffer' [-Wvla]
10 | char buffer[size];
| ^~~~~~
628

被折叠的 条评论
为什么被折叠?



