I was recently investigating a bug and would like to share an unexpected, yet interesting discovery regarding the cause of the issue. In order to provide an idea of what I was working on, consider the following code as a simplified representation:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void read_to_buffer(std::size_t size) {
char buffer[size];
std::memset(buffer, 0, size);
// call some function to fill the buffer
// process the content of the buffer
// to make sure that the function is not compiled out
std::cout << buffer << std::endl;
}
int main(int argc, char *argv[]) {
auto size = std::stoul(argv[1]);
read_to_buffer(size);
return 0;
}
In my case, the code was working perfectly, until the size
was changed somewhere deep in the configuration. After that, it started to crash due to a segmentation fault, as the code was trying to allocate too much memory on the stack. The solution is to simply allocate the memory on the heap. Fortunately, C++ provides plenty of options, such as std::vector v(size)
, std::string s(size, 0)
, std::unique_ptr(new char[size])
, and so forth.
In my case, the code was working perfectly, until the size
was changed somewhere deep in the configuration. After that, it started to crash due to a segmentation fault, as the code was trying to allocate too much memory on the stack. The solution is to simply allocate the memory on the heap. Fortunately, C++ provides plenty of options, such as std::vector v(size)
, std::string s(size, 0)
, std::unique_ptr(new char[size])
, and so forth.
My only question was, “How was the code successfully compiled in the first place?”
According to the C++ standard, the size of objects allocated on stack must be known at compile time. In the sample I mentioned above, char buffer[size]
is a variable-length array, which is actually a feature from the C99 standard, and not related to C++.
The interesting catch is that while the variable-length array is not supported by the C++ standard, GCC and Clang still attempt to compile it because they innately comply with both standards.
The interesting catch is that while the variable-length array is not supported by the C++ standard, GCC and Clang still attempt to compile it because they innately comply with both standards.
I can offer the following recommendation in order to avoid such a subtle nuisance: the variable-length arrays need to be explicitly disabled by adding -Werror=vla
to the CXX_FLAGS
.
The optimal and safest solution would be to use the -pedantic
flag, which instructs the compiler to adhere to the C++ standard and forbids all extensions.
The optimal and safest solution would be to use the -pedantic
flag, which instructs the compiler to adhere to the C++ standard and forbids all extensions.
And now, ladies and gentlemen,
Exhibit A:
1
2
3
4
5
$ make
/home/user/example/main.cpp:7:16: error: variable length arrays are a C99 feature [-Werror,-Wvla-extension]
char buffer[size];
^
1 error generated.
Exhibit B:
1
2
3
4
5
$ make
/home/user/example/main.cpp:7:21: error: ISO C++ forbids variable length array ‘buffer’ [-Werror=vla]
char buffer[size];
^
cc1plus: all warnings being treated as errors
Update:
Recently I discovered that this post inspired a change in the Clang compiler’s defaults: -Wvla-extension
diagnostic group in C++ language modes was enabled by default, and the warning
group was added to -Wall
in GNU++ language modes.
Please share your thoughts on LinkedIn.