There is an old post asking for a construct for which sizeof would return 0. There are some high score answers from high reputation users saying that by the standard no type or variable can have sizeof 0. And I agree 100% with that.
However there is this new answer which presents this solution:
struct ZeroMemory {
int *a[0];
};
I was just about to down-vote and comment on it, but time spent here taught me to check even the things that I am 100% sure on. So... to my surprise both gcc and clang show the same results: sizeof(ZeroMemory) == 0. Even more, sizeof a variable is 0:
ZeroMemory z{};
static_assert(sizeof(z) == 0); // Awkward...
Whaaaat...?
Godbolt link
How is this possible?
Before C was standardized, many compilers would have had no difficulty handling zero-size types as long as code never tried to subtract one pointer to a zero-size type from another. Such types were useful, and supporting them was easier and cheaper than forbidding them. Other compilers decided to forbid such types, however, and some static-assertion code may have relied upon the fact that they would squawk if code tried to create a zero-sized array. The authors of the Standard were faced with a choice:
Allow compilers to silently accept zero-sized array declarations, even
in cases where the purpose of such declarations would be to trigger a
diagnostic and abort compilation, and require that all compilers accept
such declarations (though not necessarily silently) as producing zero-
sized objects.
Allow compilers to silently accept zero-sized array declarations, even
in cases where the purpose of such declarations would be to trigger a
diagnostic and abort compilation, and allow compilers encountering such
declarations to either abort compilation or continue it at their leisure.
Require that implementations issue a diagnostic if code declares a
zero-sized array, but then allow implementations to either abort
compilation or continue it (with whatever semantics they see fit) at
their leisure.
The authors of the Standard opted for #3. Consequently, zero-sized array declarations are regarded by the Standard "extension", even though such constructs were widely supported before the Standard forbade them.
The C++ Standard allows for the existence of empty objects, but in an effort to allow the addresses of empty objects to be usable as tokens it mandates that they have a minimum size of 1. For an object that has no members to have a size of 0 would thus violate the Standard. If an object contains zero-sized members, however, the C++ Standard imposes no requirements about how it is processed beyond the fact that a program containing such a declaration must trigger a diagnostic. Since most code that uses such declarations expects the resulting objects to have a size of zero, the most useful behavior for compilers receiving such code is to treat them that way.
As pointed out by Jarod42 zero size arrays are not standard C++, but GCC and Clang extensions.
Adding -pedantic produces this warning:
5 : <source>:5:12: warning: zero size arrays are an extension [-Wzero-length-array]
int *a[0];
^
I always forget that std=c++XX (instead of std=gnu++XX) doesn't disable all extensions.
This still doesn't explain the sizeof behavior. But at least we know it's not standard...
In C++, a zero-size array is illegal.
ISO/IEC 14882:2003 8.3.4/1:
[..] If the constant-expression (5.19) is present, it shall be an integral constant expression and its value shall be greater than zero. The constant expression specifies the bound of (number of elements in) the array. If the value of the constant expression is N, the array has N elements numbered 0 to N-1, and the type of the identifier of D is “derived-declarator-type-list array of N T”. [..]
g++ requires the -pedantic flag to give a warning on a zero-sized array.
Zero length arrays are an extension by GCC and Clang. Applying sizeof to zero-length arrays evaluates to zero.
A C++ class (empty) can't have size 0, but note that the class ZeroMemory is not empty. It has a named member with size 0 and applying sizeof will return zero.
Related
As we already know, VLA (standardized in C99) are not part of the standard in C++.
So the code below is "illegal" in C++:
void foo(int n) {
int vla[n];
for (int i = 0; i < n; ++i) {
vla[i] = i;
}
}
Despite of that the compiler (g++ and clang++) accepts the code as valid syntax, producing just a warning in case -pedantic flag is enable.
ISO C++ forbids variable length array ‘vla’ [-Wvla]
My questions are:
Why does the compiler accept that declaration?
The compiler cannot just reject an array in which length [is-no-know-at-compile-time]?
Is there a sort of compatibility syntax rule to follow?
What does the standard say about?
From the assembly code produced I see the compiler writes in the stack
in the loop, like a normal array, but I cannot find anything about the standard behaviour.
Why does the compiler accept that declaration?
Because its authors chose to make it do so.
GCC in particular allows, by default, a lot of non-standard stuff that was historically accepted by old C compilers. They like "compatibility" in that sense.
What does the standard say about [it]?
Precisely what the warning states it says about it: ISO C++ forbids variable length arrays.
C++ does not have VLAs.
Where you see one being accepted, it is a compiler extension; to find out how that compiler implements such an extension, you would have to ask the compiler's authors (or examine its source, if applicable).
The standard requires that a conforming compiler must "issue a diagnostic" when it encounters something that is illegal. Having done that, it's free to continue to compile the code with an implementation-specific meaning. (Note that "with an implementation-specific meaning" is a polite form of "with undefined behavior").
I am doing the following for initializing an array in c++
int a;
cin>>a;
float b[a];
This works and compiles in my computer. IS this correct? I thought that we can only do this if a was a const int.
Depends on you definition of "correct".
This is called variable-length array (or just VLA) and it's not officially supported in the current versions of C++ (100% sure for C++03 and before, 99.99% sure for C++11), but it is in C.
Some compilers allow this as a compiler extension.
It's not about whether a is a constant int. It's about whether a has a initial value assigned at comipling time. Compiler needs to allocate storage according to a const int value. C++ standard doesn't support variable length array right now.
In C99, this syntax of variable length array is valid, but C++ standard says no. It is a very useful feature, leaving all the hairy memory allocating stuff to the compiler.
In GCC and Clang, this feature is supported as a compiler extension, so you won't get any warning and error. But MSVC compiler will put an error message that says cannot allocate an array of constant size 0, So it is compiler specific.
The compiler that supports this feature may have convert your code with new operator.
int a;
cin>>a;
float *b = new float[a];
This is valid in C++ standard.
Another thing is that though it is called variable-length array, it is not length-variable at all. Once it is defined, its length is a constant value which never change. You can't expand it or shrink it.
It is much better to use the vector container which is truly length variable, and with much more scalability and adaptivity.
See the post for more discussion on Why aren't variable-length arrays part of the C++ standard?
Can C++11 compilers (and do they) notice that a function is a constexpr and treat them as such even if they are not declared to be constexpr?
I was demonstrating the use of constexpr to someone using the example straight from the Wikipedia:
int get_five() {return 5;}
int some_value[get_five() + 7]; // Create an array of 12 integers. Ill-formed C++
To my surprise the compiler was OK with it. So, I further changed get_five( ) to take a few int parameters, multiply them and return the result while still not being explicitly declared to be constexpr. The compiler was OK with that as well. It seems that if the compiler can do this there isn't much point to having the restrictions that are required in order to explicitly declare something constexpr.
On a properly-functioning C++11 compiler, your code would be rejected.
Based on its being accepted, you're almost certainly using gcc (or something that closely emulates its bugs). gcc [depending somewhat on flags] can accept array sizes that aren't constant by any measure (e.g., depend on run-time input from the user) because they support an analog of C99 variable-length arrays in C++.
GCC, as of GCC 12, supports the -fimplicit-constexpr command-line toggle which enables exactly that, for methods marked as inline.
Changelog: https://gcc.gnu.org/gcc-12/changes.html
Patch with some rationale and discussion: https://gcc.gnu.org/git/?p=gcc.git;a=commitdiff;h=87c2080b
A compiler can detect whether or not a function could have been declared with constexpr even when they haven't, for optimization purposes (i.e. computing the result of a function at compile-time). Compilers did that prior to C++11.
But for use in places that requires constant expressions, such as template parameters of integral type, it is against the standard to allow calls to functions that are not declared with the constexpr keyword.
I am getting the sizeof of object as zero, which is ought not to be. Please explain me the concept as why the compiler is giving this answer?
#include<iostream>
using namespace std;
class xxx{
public: int a[]; // Why this line is not giving error.
};
int main(int argc, char *argv[])
{
xxx x1;
cout<<sizeof(x1); //Q=Why this code is not giving error.
return 0;
}
As the others have said, an object in C++ can never have size 0.
However, since the code isn’t valid C++ in the first place (arrays cannot be empty) this is inconsequential. The compiler just does what it wants.
GCC with -pedantic rejects this code. MSVC at least warns. My version of clang++ with -pedantic ICEs but does emit a warning before that.
You're not using a standard-compliant compiler. An object size can't be 0, even an empty class or struct has size 1. Moreover, the array dimension has to be specified.
EDIT: It's strange, ideone also prints out 0. In MSVS I get a warning, but at least the size is 1.
5.3.3. Sizeof
[...] When applied to a class, the result is the number of bytes in an object of that class [...] The size of a most derived class shall
be greater than zero. [...] The result of applying sizeof to a base class subobject is the size of the base class type. [...]
EDIT 2:
I tried the following in MSVS:
xxx a[100];
and it fails to compile. Strange how it doesn't pick up the error beforehand.
That element a in your class xxx is called a flexible array member.
Flexible array members are not in the C++ standard. They are a part of C99. However, many compiler vendors provide flexible array members as a C++ extension.
Your code as-is is not legal C code. It uses C++ specific constructs. Your code is easy to change to C. Change the class to struct, get rid of the public, and change the use of C++ I/O to C's printf. With those changes, your converted code is still illegal C99 code. Flexible array members are only allowed as the last element of a structure that is otherwise non-empty.
Apparently your vendor took the flexible array member concept over to C++, but not the constraint that the structure be otherwise non-empty.
The size of an object can not be zero. even if the class is empty, its size is never zero.
Checkout the link to know more Bjarne Stroustrup's C++ Style and Technique FAQ.
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
array initialization, is referencing a previous element ok?
I wonder if its safe to do such initialization in c/c++ standard:
int a = 5;
int tab[] = { a , tab[0] + 1 , tab[1] };
It successfully compiles and executes with gcc 4.5 and clang 2.9, but will it always be true?
Printing this table gives 5 6 6. Its initialized in global scope.
Generally its interesting in both, c and c++, but i want to use it in c++:)
C++03/C++11 answer
No, it won't.
On the right-hand side of the =, tab exists1 but — if it has automatic storage duration — it has not yet been initialised so your use of tab[0] and tab[1] is using an uninitialised variable.
If tab is at namespace scope (and thus has static storage duration and has been zero-initialized), then this is "safe" but your use of tab[0] there is not going to give you 5.
It's difficult to provide standard references for this, other than to say that there is nothing in 8.5 "Initializers" that explicitly makes this possible, and rules elsewhere fill in the rest.
1 [n3290: 3.3.2/1]: The point of declaration for a name is immediately after its complete declarator (Clause 8) and before its initializer (if any) [..]
int a =5;
int tab[] = { a , tab[0] + 1 , tab[1] };
If these variables are declared at namespace scope, then they're okay, as at namespace scope variables are zero-initialized (because of static initialization - read this for detail).
But if they're declared at function scope, then second line invokes undefined behaviour, since the local variables are not statically initialized, that means, tab[0] and tab[1] are uninitialized, which you use to initialize the array. Reading uninitialized variables invokes undefined behavior.
In the C99 standard, it seems that the order of initialization of the members is guaranteed:
§6.7.8/17: Each brace-enclosed initializer list has an associated current object. When no designations are present, subobjects of the current object are initialized in order according to the type of the current object: array elements in increasing subscript order, structure members in declaration order, and the first named member of a union. In contrast, a designation causes the following initializer to begin initialization of the subobject described by the designator. Initialization then continues forward in order, beginning with the next subobject after that described by the designator.
But as #Tomalak mentions in the comment, that does not provide a full guarantee for the operation, as the compiler would first evaluate all of the arguments and then apply the results in the previous order. That is, the previous quote does not impose an order between the initialization of tab[0] and the evaluation of the expression tab[0]+1 that is used to initialize tab[1] (it only imposes an ordering between the initialization of tab[0] and tab[1])
As of the C++ standard, neither in the current standard nor the FDIS of the upcoming C++0x standard seem to have an specific clause defining the order in which the initialization is performed. The only mention of ordering comes from
§8.5.1/2 When an aggregate is initialized the initializer can contain an initializer-clause consisting of a brace-enclosed, comma-separated list of initializer-clauses for the members of the aggregate, written in increasing subscript or member order.
But that only relates to the order by which the entries in the initializer are written, not how it is actually evaluated.
so, now I've run a couple of tests regarding your problem.
All compilations have been performed with your example code above, using the following scheme:
$(GCC) -o a.out test.c -Wall -Wextra -pedantic -std=$(STD)
This yielded the following results:
for GCC = gcc, the standards -std=c89; -std=iso9899:1990; -std=iso9899:199409; -std=gnu89 resulted in a warning showing up: initializer element is not computable at load time and undefined behaviour at runtime, meaning that the second and third value of the array were random garbage.
the standards -std=c99; std=iso9899:1999; -std=gnu99 did not produce this warning, but also showed undefined behaviour at runtime.
for GCC = g++, the standards -std=c++98; -std=gnu++98; -std=c++0x produced no warning, and the code worked as you'd expected it to, resulting in an array containing the values {5, 6, 6}.
However, as most of the people advised, it might be unwise to use this, since your code might behave differently on other compilers, or maybe even other versions of the same compiler, which is generally a bad thing :)
hope that helped.
Yes - It will probably work as you expect it to be.
No - (you didn't ask but) Don't use it, it make no logic and it's a bad practice.