I've found a strange looking piece of code in a project I have to maintain. There's an empty array member of a class which doesn't lead to an compiler error. I've tested some variations of such a code with MSVC 10.0:
template<class T> struct A {
int i[];
}; // warning C4200: nonstandard extension used : zero-sized array in struct/union
template<class T> struct B { static int i[]; };
template<class T> int B<T>::i[];
struct C {
int i[];
}; //warning C4200: nonstandard extension used : zero-sized array in struct/union
template<class T> struct D { static int i[]; };
template<class T> int D<T>::i[4];
template<> int D<int>::i[] = { 1 };
int main()
{
A<void> a;
B<void> b;
C c;
D<void> d0;
D<int> d1;
a.i[0] = 0; // warning C4739: reference to variable 'a' exceeds its storage space
b.i[0] = 0; // warning C4789: destination of memory copy is too small
c.i[0] = 0; // warning C4739: reference to variable 'c' exceeds its storage space
int i[]; // error C2133: 'i' : unknown size
d0.i[0] = 0; // ok
d0.i[1] = 0; // ok
return 0;
}
The error message at int i[] is absolutely sensible to me. The code which is shown with class D is well-formed standard C++. But what's about the classes A, B and C? What kind of types are the member variables int i[] in this classes?
EDIT:
your doubt is explained by the definition of the extension to the language, which allows for zero-sized arrays at the end of structs/unions. I have not tried it, but if you declare another member after the zero-sized array, it should fail.
so, if you allocate a variable on the stack, you have to know its size; the exception to the rule is when allocating an array at the end of a struct/union, where some C-typical trickery is possible.
In c++ this raises a warning because the default copy constructor and assignment operator will probably not work.
PREVIOUS ANSWER:
The compiler warns you about the fact that you are trying to define an array with zero size. This is not allowed in standard C/C++.
Let's see the differences class by class.
In class D:
template<class T> struct D { static int i[]; };
it works because you are just declaring the type of a static member variable. For this to link, you need also defining the actual array, in a definition statement like you do:
template<> int D<int>::i[] = { 1 };
here you also specify the size of the array through the initializer.
With class B, you are doing something similar, but the definition is:
template<class T> int B<T>::i[];
i.e., you don't specify the size and get the warning.
With class A, more of the same, you are defining a member variable of type array without the size.
Good one. Just to be certain, you are wondering why the compiler isn't flagging it as an error right? In that case, I think this problem is unpredictable across compilers but I'm aware of this happening on MSVC all the time.
http://support.microsoft.com/kb/98409
Let me see if I can explain it like they did. If I were to declare a struct with an empty array like this,
struct a
{
int x;
char empty[];
};
the compiler might allocate 4 bytes for x and probably another 4 bytes for the char pointer. empty will contain the address 4 bytes past the start of struct a.
Since it is a character array of no length, trying to access it would be an error since there is no trailing 0 to signify the end of the string.
I could choose to initialize the struct later to point to the start of an actual string to overcome this error.
struct a myStruct = { 1, "hello world"}; // empty now points to the start of "hello world"
Since a struct is basically a class, turns out you can do the same thing with a class if you make sure its an aggregate and not a full class.
So there ya go. MSVC compilers treat arrays with no fixed sized as a pointer when declared within a struct/class. Remember that class definitions are merely just declarations. The compiler doesn't allocate space for them until you create an instance for it. When you start to think about it, it sorta makes since. How will the compiler know if you plan to allocate storage for it later. It becomes a run-time artifact but the compiler was still smart enough to warn you about the problem.
Related
Assuming I've got the following struct A definition from a subcontractor:
struct A {
int var0;
int var1;
int var2;
};
I cannot change anything about it but I have to use this struct for the call to the subcontractors API. In my calculation components I'll use a more generic version called struct B:
struct B {
int var[3];
int other_vars[3];
// [...]
};
Given these, I am looking for a simple way to map the array var from my more generic struct B to the explicit variable declarations of struct A.
The current implementation is simple as stupid
a.var0 = b.var[0];
a.var1 = b.var[1];
a.var2 = b.var[2];
which produces a very large mapping file and perhaps failures in the future, if struct A gets an update.
Possible Solutions:
I thought about something like memcpy, but I think that is very unsafe..
#define MAP(from, to, var) \
std::memcpy(&to.var##0, &from.var, sizeof(from.var));
MAP(b, a, var);
Online GDB with this example
Sidenotes:
The structs are much bigger than shown here. There are a bunch of different variables defined that way with much higher indexes.
Due to the fact that the code for struct A is generated by the subcontractor (we get the generated .h file), I cannot guarantee that the variables are in the right order and not intermitted by other variables. That's why my possible solution is not good enought in my opinion.
As long as the members of struct A map to struct B::var in the same order, memcpy is the best way to do this. B both structs are 'standard-layout' types, they are guaranteed to occupy contiguous bytes of storage.
From C++ standard :
An object of trivially copyable or standard-layout type shall occupy contiguous bytes of storage.
To deal with any potential padding issues that may make this process unsafe; static assertions can be used to ensure everything lines up as expected.
#include <type_traits>
struct A {
int var0;
int var1;
int var2;
// int var3; // Adding this variable will cause static_assert to fail.
};
struct B {
int var[3];
int other_vars[3];
// [...]
};
// Note: for B to be trivially-copyable or standard-layout, all the members must also be.
static_assert( std::is_trivially_copyable<A>::value);
static_assert( std::is_trivially_copyable<B>::value);
static_assert( std::is_standard_layout<A>::value);
static_assert( std::is_standard_layout<B>::value);
static_assert(sizeof(A) == sizeof(B::var), "Incompatible mapping to subcontractor.");
Again, this only works so long as struct A doesn't change so that A::var0 should now map to B::var[1] or some non-contiguous order.
I tried to set array size with const member variable like below
class A {
const int SIZE;
A() : SIZE(10) {}
void aaa() { int a[SIZE]; }
};
I cannot build
a[SIZE]
like this expression.
When I use GCC, build success. But when I use VC++ (Windows), I cannot build.
The error message is "'this' cannot be used in a constant expression"
How to set array size with const member variable?
Variable Length Arrays
when I use VC++ (Windows), I cannot build.
An array int a[SIZE] is created on stack with automatic storage duration.
Then this SIZE usually must be determined at compile-time in C++.
But your SIZE is defined without a value as follows:
const int SIZE;
Thus compilers can not know it's value at compile-time and would show compilation error.
So your SIZE is a dynamic variable.
When I use GCC, build success.
...But some C++ compilers support VLA (variable length arrays) which is a C99 addition and allows declaring C-style arrays on stack with a dynamic length.
VC++ does not support C99 and VLA, but GNU compiler supports VLA as an extension even in C90 and C++.
This is the reason why you can compile the above code by GCC without errors.
If you add -pedantic (-pedantic-errors) option to gcc compile command, we can get warnings (errors) for most gcc extensions.
In this case, with this option we should get warning (error) message: ISO C++ forbids variable length array 'a'.
How to fix the current error ?
(1) If you want to use C-style arrays, one way is making SIZE a macro as follows. Then compilers can know it's value at compile-time:
#define SIZE 10
class A
{
public:
A() {}
void aaa() { int a[SIZE]; }
};
(2) Defining the value of SIZE as a static const variable in the class definition, then compilers again can know it's value at compile-time:
class A
{
static constexpr int SIZE = 10;
public:
A() {}
void aaa() { int a[SIZE]; }
};
(3) C++ provides array functionality by std::vector and std::array which might make our code more readable, portable and robust.
I expect that std::array on stack would be more efficient than std::vector in your case, because 10 ints need less memory and std::array is allocated on stack just only once without causing problems.
This is a solution with std::array:
#include <array>
class A
{
static constexpr int SIZE = 10;
public:
A() {}
void aaa() { std::array<int, SIZE> a; }
};
Well, SIZE is probably not a macro because, here, you initialize it with 10 and have it as a variable. If it was a macro, your expression int a[SIZE] would work but you would not need lines 2-4 anyways.
If SIZE is, as here, a normal variable (which includes dynamically creating A with a size parameter, although the input might be from a macro), you should use a vector.
#include <vector>
class A {
const int SIZE;
A() : SIZE(10) {}
void aaa() { std::vector<int> a(SIZE); }
};
Usage of a is now the same.
If you are using an older compiler pre c++11, e.g c++03 you can also do enum {SIZE=10};
class A {
enum {SIZE=10};
void aaa() {int a[SIZE];}
};
Why am I getting this:
error: invalid application of 'sizeof' to incomplete type 'A'
when I try to do this:
class A
{
int m_i;
static char m_j[sizeof(A)];
};
I was under the assumption that it is obviously clear that sizeof(A) will be sizeof(int) in this case. But my compiler doesn't seem to think so.
As the error message suggests, sizeof can only operate on complete types. At the point you invoke it, A is not complete.
The C++ compiler analyzes your code from top to bottom. Your class is not completely defined at the point you invoke sizeof, therefore, the size is not yet determinable. For instance, what if another member declaration was below that line:
class A
{
int m_i;
static char m_j[sizeof(A)]; // Uh oh, m_i2 is not known here, yet
int m_i2;
};
Due to problems like this, you can use sizeof only after the class is completely defined, i.e., after its closing curly brace.
It is possible if you use a template :
template <class = void>
struct A {
int i;
static auto& m_j() {
static char arr[sizeof(A)];
return arr;
}
};
int main()
{
auto array = A<>::m_j();
}
The compiler waits until the use of m_j to instantiate the function, at which point A has been fully declared.
Live example here
We all know that sizeof an empty class or an object of empty class will be 1 byte.
I came across something where sizeof a class and its object is coming as 0. The program is syntactically correct as there were no compilation or run time errors. Is this undefined behavior? The use case I'm trying to execute makes any sense and looks like a valid one? Is it a big blunder to not to give exact subscript or size for an array in the class? The code snippet is as below:
#include<iostream>
using namespace std;
class A
{
char a[];
};
int main()
{
A b;
cout<<sizeof(A)<<endl;
cout<<sizeof(b)<<endl;
return 0;
}
output:
0
0
The sizeof an empty class is one byte (non zero basically) and the reason for that is said like "To make sure that different objects have different addresses".
What happens in this case then when sizeof class is coming a zero?
Note: Observed the same behavior for int a[] as well.
It's called "flexible array member" and it's a feature of C99 (I think). It's not valid C++ - you don't have warnings/errors, probably because the compiler supports it as an extension.
Compiling with -Wall -Wextra -pedantic -std=c++NN (98, 03, 11, 14, ..) should generate warning (the last two flags will disable any compiler extensions).
You can see some information in this related question: Is using flexible array members in C bad practice?
For example, here's what GCC says about this:
In ISO C99, you would use a flexible array member, which is slightly different in syntax and semantics:
...
Flexible array members have incomplete type, and so the sizeof operator may not be applied. As a quirk of the original implementation of zero-length arrays, sizeof evaluates to zero.
(source: https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html).
This explains the 0 size of char a[] and not the 0 for the class, but as I already mentioned - it's a C feature and not a valid C++.
If you compile with -pedantic flag
$ g++ -W -Wall -pedantic prog.cpp prog.cpp:5:11: warning: ISO C++
forbids zero-size array ‘a’ [-pedantic]
C++ does not support VLAs and thus your class declaration is not legal and going outside the scope of standard C++ rules.
Your code is not standard C++, thus I can not see any sense in that.
If you use pedantic flag, you should receive this:
gsamaras#pythagoras:~$ g++ -pedantic file.cpp
file.cpp:5:11: warning: ISO C++ forbids zero-size array ‘a’ [-Wpedantic]
char a[];
^
Try changing your class to
class A {
char a[5];
};
then you should get an output of
5
5
like you should expect.
However, you can argue that without the flag, your code does compile and outputs zeroes. As a counter I could say that the same goes if you use this class:
class A {
char a[0];
};
but I am pretty sure you know that zero-sized arrays are not allowed, but still this thing compiles fine and gives an output of zeroes.
Empty base classes can be optimized to zero bytes, which would technically make sizeof(base) also 0.
The "1 byte" thing is really an implementation detail, coming from the rule that distinct objects need to have distinct addresses.
So:
struct base { };
struct derived : base { };
Both sizeof(base) and sizeof(derived) are allowed to be 0, because the derived object is the same object as the base object contained within.
However:
struct base1 { };
struct base2 { };
struct derived : base1, base2 { };
Here, sizeof(derived) must be 1, because the standard requires that
derived d;
assert(static_cast<base1 *>(&d) != static_cast<base2 *>(&d));
Similarly:
struct type1 { };
struct type2 { };
struct combined { type1 obj1; type2 obj2; };
requires that
combined c;
assert(&c.obj1 != &c.obj2);
Many compiler vendors take the shortcut and simply make empty classes take up one byte.
The size of a class can be 0. Consider the following piece of code
#include <iostream>
using namespace std;
class A
{
public:
int a[0];
void getA(){
cout<<"Hello World";
}
};
class B
{
};
int main()
{
cout<<"The size of A is "<<sizeof(A)<<endl; // prints 0
A w;
cout<<"The size of object of A is "<<sizeof(w)<<endl; //prints 0
cout<<"The size of the array a in A is "<<sizeof(w.a)<<endl; // prints 0
cout<<"The value from function of class A is "<<w.getA()<<endl; // Gives a compilation error
cout<<"The size of B is "<<sizeof(B)<<endl; //prints 1
}
Output:
The size of A is 0
The size of object of A is 0
The size of the array a in A is 0
The size of B is 1
So, accessing functions present in the class with a size 0 leads to compilation error.
I want to initialize constant in child-class, instead of base class. And use it to get rid of dynamic memory allocation (I know array sizes already, and there will be a few child-classes with different constants).
So I try:
class A {
public:
const int x;
A() : x(0) {}
A(int x) : x(x) {}
void f() {
double y[this->x];
}
};
class B : A {
B() : A(2) {}
};
Pretty simple, but compiler says:
error C2057: expected constant expression
How can I say to compiler, that it is really a constant?
It isn't a constant though. It can still be modified by the constructor. Only a compile time constant is allowed for the size of an array. When the compiler says "constant expression", it is not meaning an expression which returns a constant value, but an constant, such as "52" or "45" or something along those lines.
Use std::vector instead.
EDIT: In response to "I know array sizes already, and there will be a few child-classes with different constants"
The only way to do that is to use a template.
template<size_t x>
class A {
public:
void f() {
double y[x];
}
};
typedef A<2> B;
The behaviour you expect could be achieved using the following template.
Note that this is actually unreliable, disgusting and could be used only as "a sample". Use std::vector instead.
template <size_t a = 0>
class A {
public:
A() { }
void f() {
int y[a];
y[0] = 5;
}
};
class B : A<2> {
B() { }
};
void main() {
A<1> a;
a.f();
// Undefined behaviour - creating an array of size 0
// At least, MSVS2008 treats it as an error :)
// A<0> a_;
}
There's "constant", and then there's "constant". If you want to allocate an array on the stack like that, the compiler needs the length of the array at compile time, and based on what you've given there it can't figure that out. Interestingly, gcc supports an extension (not supported in standard C++) that allows for stack allocation for variable lengths.
I don't know if it will work for your purposes, but one possibility would be to make it a template parameter:
template <int size>
class A {
double y[size];
};
In this case, you'd probably want to create an instance of A in B instead of using inheritance.
The other obvious possibility would be to use a tr1::array object instead. This is is also a template, so the idea is pretty much the same, but it's already written, tested and working so you can avoid all that. If your compiler doesn't supply TR1 classes, Boost has a mostly conforming implementation (boost::array).