In the following code:
#include <cstring>
template <unsigned len>
struct CharArray {
CharArray() {
memset(data_, 0, len);
}
char data_[len];
};
struct Foobar {
CharArray<5> a;
CharArray<3> b;
CharArray<0> c;
};
int main() {
Foobar f;
}
The type CharArray<0>
ends up having a zero-sized array as its only member. I'm aware of this being a GCC extension and unsafe practice in general. The question is not about that.
When I compile the code with gcc 10.2.0, I get the following warning:
<source>: In function 'int main()':
<source>:5:3: warning: array subscript 8 is outside array bounds of 'Foobar [1]' [-Warray-bounds]
5 | CharArray() {
| ^~~~~~~~~
<source>:18:10: note: while referencing 'f'
18 | Foobar f;
| ^
With gcc9 and earlier there's no warning.
Question: Where does the subscript 8 come from? And what is the Foobar [1]
mentioned there? It looks like there's an array of one Foobars and we're trying to access element 8 in that array. Not sure how that could happen. If somebody knows the details, I'd appreciate it if you could explain it.
This happens when compiling with gcc++-10
in Ubuntu 20.04 with -O3 -Wall -Wextra
as options. If I don't pass any optimization flag, there won't be any warning. Also: if I take the constructor away, the warning will also disappear.
It seems the issue is somehow related to the memset()
: as avoiding it using a condition (len != 0
) doesn't work it seems the compiler recognizes that the start address of CharArray<0>
's object is produced by the intialization of CharArray<3>
and warns about that. This theory can be tested by conditionally not initializing CharArray<3>
with memset()
or specializing that type as that makes the warning go way:
CharArray() { if (len != 3) memset(data_, 0, len); }
or
template <>
struct CharArray<3> {
CharArray(): data_() { }
char data_[3];
};
The warning is probably spurious. It seems by the time the address of the zero sized array is used the compiler has "forgotten" that it was produced by accessing a different array's member. The easiest approach to avoid the warning seems to correctly initialize the data
in the initializer list and not using memset()
at all:
template <unsigned len>
struct CharArray {
CharArray(): data_() {}
char data_[len];
};
Doing anything to a zero length C-like array is higly suspicious. Including even defining one in my opinion.
However, you can specialise the constructor to NOT do anything to the zero length array:
template<> CharArray<0>::CharArray() {}
In order to not even define a zero sized array (which I think should not be an obstacle to anythign you might want to achieve with the class in general...), you would have to specialise the whole class. (credits to Dietmar Kühl for this addition)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With