GCC gives no error when you initialize a variable-sized array as long as the variable is const, but when it isn't, it won't compile.
What's the reason behind this? What's so wrong with doing:
int size = 7;
int test[size] = {3, 4, 5};
That won't compile at all, but if I don't initialize test[] then it does compile! That doesn't make any sense to me, because as far as I know a stack frame needs to be made to fit this array according to its size(7 ints) no matter what(which means the integer literals I use don't really have any meaning, if I'm not mistaken), so what difference does it make if I initialize it or not?
Just another one of my crazy C++ design questions...
Thanks!
The size of the array must be a constant integral expression.
An integral literal is a constant integral expression. (int arr[5];)
A constant integral variable initialized with a constant expression is a constant expression. (const int j = 4; const int i = j; int a[i];)
A constant variable initialized with a non-constant expression is not a constant expression
int x = 4; // x isn't constant expression because it is not const
const int y = x; //therefore y is not either
int arr[y]; //error)
It's actually more like a crazy C99 design question, since variable-length arrays are a feature from C99 that gcc allows in C++ as an extension.
In C99, 6.7.8/3 says "The type of the entity to be initialized ... is not a variable length array type", so gcc has just used the same rule for its extension as is required by C99.
The C99 rationale document doesn't say anything about why a VLA can't be initialized. I can speculate that it might be because of the risk of excess elements in the initializer, if the value provided for the size turns out to be smaller than the initializer. But I don't know.
Some compilers allow this if you use const int size = 7;. Theoretically the compiler could figure out that it's constant size but it doesn't do that.
From http://gcc.gnu.org/onlinedocs/gcc/Variable-Length.html "Variable-length automatic arrays are allowed in ISO C99, and as an extension GCC accepts them in C90 mode and in C++. "
Your program is not valid in c++ at all and gcc compiles it as "an extension". You'd likely have to ask the authors of gcc why they decided to implement in this way.
The code is not valid in standard C++.
As per (8.3.4.1) of the C++ standard, array size must be a constant expression
Variable lenght arrays are not allowed in C++ because C++ provides std::vector for that.
Variable length array was a feature introduced in C99 after C++ branched out from C standard based on c98. C++ already had std::vector to provide functionality of variable lenght arrays so C++ standard never allowed variable length arrays as a part of the standard.
If your compiler supports it, it is through a compiler extension. Compile with -pedantic option and it will notify you of the same with the warning saying it's forbidden by ISO C++
I'm not sure about the intentions of gcc designers when they implemented this extension, but one possible reason why the gcc extension works like this is:
int is1[2] = {1}
compiles without warning, reasonable to assume user wants {1,0}
int is2[1] = {1,2};
compiles with a warning, what should the compiler do?
int i;
cin >> i;
int is3[i] = {1,2}
aha, to warn or not to warn?
Related
I have doubt regarding the following piece of code :
int main()
{
int array1 = {1,2,3,4,5}; //error in c++ , warning in c
int array2[] = {1,2,3,4,5};
int array3[5] = {1,2,3,4,5};
}
This piece of code gives a error on line 3 in c++ but not in c?
I know array1 is actually an int and array2 and array3 are arrays, so why doesn't a c compiler show a error , but just a warning: "excess elements in scalar initialization"
Is there a use of such a definition and why is it valid in c?
It is not valid C. See C11 6.7.9:
No initializer shall attempt to provide a value for an object not
contained within the entity being initialized.
I would guess that you are using gcc. Then if you want your program to behave as strict standard C, compile it as such:
gcc -std=c11 -pedantic-errors
gives
error: excess elements in scalar initializer
It isn't valid in C. It just has less checks on the code. This is Undefined behaviour.
From: C11 draft N1570; 6.7.9 Initialisation
Constraints
2 No initializer shall attempt to provide a value for an object not contained within the entity being initialized.
3 The type of the entity to be initialized shall be an array of unknown size or a complete object type that is not a variable length array type.
Definitely breaks constraint 2. Is an int a complete object type?
from Annex J.2 (undefined behaviour):
The initializer for a scalar is neither a single expression nor a single expression enclosed in braces (6.7.9).
extra:
#James Kanze:
prog.c:4:12: error: expected identifier or ‘(’ before numeric constant
int i = 1,2,3,4,5;
^
you can do it, but you need to make it one expression:
int i = (1,2,3,4,5); //need parenthesis, evaluates to 5, values 1-4 thrown away.
Compiling with an int initialised with an initalizer-list spawns a warning (in gcc):
prog.c:5:2: warning: excess elements in scalar initializer [enabled by default]
int j = {1,2,3,4,5};
^
but it appears that the compiler is smart enough to only initialise the int and not the following memory.
demo
It's valid as per the C specification.
Quote from C11 section 6.7.9 (Initialization):
Syntax
1 initializer:
assignment-expression
{ initializer-list }
{ initializer-list , }
So an initializer can either be a direct expression (assignment-expression above), or a brace-enclose list of initializers. The constraints in that section of the standard does not limit this for "normal" (non-array or non-pointer) variables.
That allows you to write e.g.
int a = { 1 };
Like everyone else, I assume that the line you're wondering about is:
int array1 = {1,2,3,4,5};
In this case, there is no difference between C and C++; the line is arguably legal in both languages, but it doesn't mean what you might think. In both C and C++, there is a statement to the effect that if the type is a scalar type (int is), then the contents of the {...} must be a single expression. And 1,2,3,4,5 can be interpreted as a single expression (with the comma operator); something like:
int array1 = 1, 2, 3, 4, 5;
is clearly legal.
It's somewhat ambiguous, however, since in both languages, the grammar for this type of initialization makes the , punctuation, and not an operator. So it is a question of interpretation; is the statement that the contents must be a single expression a constraint on the grammar (which would cause the comma to become an operator), or a constraint on the results of evaluating the specified grammar. My intuitive feeling is that the second is the intent, and that the statement should result in an error. But the difference isn't between C and C++, but between the way the authors of the compilers are interpreting the standard.
EDIT:
On rereading a little closer: in the C++ standard, it explicitly says that
If T is a scalar type, then a declaration of the form
T x = { a };
is equivalent to
T x = a;
Which doesn't leave much wriggle room: in C++, the statement seems clearly legal; it's only in C where there is some ambiguity.
The right way to initialize an array in C/C++ is:
int array2[] = {1,2,3,4,5};
Here the square brackets actually inform the compiler that this is an array, a set of numbers in your case. In that way of initialization it is not necessary to specify the length of the array. The compiler will know.
The second way is to define an array and post initialize it:
int array3[5];
int *array = new int[5];
In this case you'll need to inform the compiler what size the array will be. Moreover, in the second case you need to delete the memory manually.
As your compiler says, this is not correct for C, either. The important thing to notice here is that in case of such "constraint violations" (the official term) the compiler may just produce a diagnostic (which your compiler did) and continue.
That is why generally you should ensure that your code compiles without any diagnostic whatsoever.
C is a low-level permissive language. It allows to affect a pointer to an int.
int array1 = {1,2,3,4,5};
EDIT :
I should have tested it under different compilers before writing stupid things.
This is rejected as error by MSVC (2008).
Both gcc and clang issue a warning excess elements in scalar initializer and simply affect 1 to a.
I tried to write a c program as below?
const int x = 5;
int main()
{
int arr[x] = {1, 2, 3, 4, 5};
}
This is giving warnings when I tried to compile with gcc as below.
simple.c:9: error: variable-sized object may not be initialized.
But the same is allowed in C++. When I pass x as array size, why x is not treated as constant?
In C const doesn't mean "constant" (i.e., evaluable at compile time). It merely means read-only.
For example, within a function, this:
const int r = rand();
const time_t now = time(NULL);
is perfectly valid.
The name of an object defined as const int is not a constant expression. That means that (in C prior to C99, and in all versions of C++) it can't be used to define the length of an array.
Although C99 (and, optionally, C11) support variable-length arrays (VLAs), they can't be initialized. In principle, the compiler doesn't know the size of a VLA when it's defined, so it can't check whether an initializer is valid. In your particular case, the compiler quite probably is able to figure it out, but the language rules are designed to cover the more general case.
C++ is nearly the same, but C++ has a special rule that C lacks: if an object is defined as const, and its initialization is a constant expression, then the name of the object it itself a constant expression (at least for integral types).
There's no really good reason that C hasn't adopted this feature. In C, if you want a name constant of an integer type, the usual approach is to use a macro:
#define LEN 5
...
int arr[LEN] = {1, 2, 3, 4, 5};
Note that if you change the value of LEN, you'll have to re-write the initializer.
Another approach is to use an anonymous enum:
enum { LEN = 5 };
...
int arr[LEN] = {1, 2, 3, 4, 5};
The name of an enumeration constant is actually a constant expression. In C, for historical reasons, it's always of type int; in C++ it's of the enumeration type. Unfortunately, this trick only works for constants of type int, so it's restricted to values in the range from INT_MIN to INT_MAX.
When I pass x as array size, why x is not treated as constant?
Because in C, constant expressions can't involve the values of any variables, even const ones. (This is one reason why C is so dependent on macro constants, whereas C++ would use const variables for the same purpose.)
On the other hand, in C++, x would certainly be a constant expression if x is declared as const int x = 5;.
If your question is why C++ is so much more liberal than C when it comes to constant expressions, I think it's to support metaprogramming, and allow complex computation to be performed at compile time using templates.
I think almost everyone has misunderstood the error, the error says:
variable-sized object may not be initialized.
which is correct, C99 and C11(although they are optional in C11). They can not be initialized in the declaration, we can see this from section 6.7.8 Initialization:
It is treated as a VLA because unlike C++, C expect an integer constnt expression:
If the size is an integer constant expression and the element type has a known constant size, the array type is not a variable length array type;
and an integer constant expression has the following restrictions:
shall have integer type and shall only have operands
that are integer constants, enumeration constants, character constants, sizeof
expressions whose results are integer constants, and floating constants that are the
immediate operands of casts. Cast operators in an integer constant expression shall only
convert arithmetic types to integer types, except as part of an operand to the sizeof
operator.
which x does not satisfy.
The type of the entity to be initialized shall be an array of unknown size or an object type
that is not a variable length array type.
In C++ this is not a variable length array since x is considered a constant expression and we can this is valid from the draft C++ standard section 8.3.4 Arrays under section 8 Declarators which says:
In a declaration T D where D has the form
D1 [ constant-expressionopt] attribute-specifier-seqopt
[...]If the constant-expression (5.19)
is present, it shall be a converted constant expression of type
std::size_t 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[...]
If we removed the const from the declaration of x it would fail for one of two reasons, either the compiler supports VLA as an extension and it would fail for the same reason it fails in C or the compiler does not support VLA as an extension and the therefore the declaration would not be valid.
I will assume you are using a C99 compiler (which supports dynamically sized arrays).
What happens is that the compiler can't know for sure in compilation time how your array will behave regarding memory.
Try this:
int arr[x];
memset( arr, 0, x*sizeof(int) );
and see if it works.
Another thing I think could be causing this is that const does not really mean anything under the hood, and so the compiler might not let you do what you are trying to because of that. You see, there are several ways you can alter const variables, and that is part of why c#, for example, does not present the const keyword.
const is more like an alert for humans than anything else.
The following code compiles under gcc 4.8 and Clang 3.2:
int main()
{
int size = 10;
int arr[size];
}
8.3.4/1 of the C++ Standard says that the size of an array must be an integral constant expression, which size does not seem to be. Is this a bug in both compilers, or am I missing something?
The latest VC++ CTP rejects the code with this interesting message:
error C2466: cannot allocate an array of constant size 0
The interesting part is how it seems to think that size is zero. But at least it rejects the code. Shouldn't gcc and Clang do the same?
This is variable length arrays or VLA which is a C99 feature but gcc and clang support it as an extension in C++ while Visual Studio does not. So Visual Studio is adhering to the standard in this case and is technically correct. Not to say that extensions are bad, the Linux kernel depends on many gcc extensions, so they can be useful in certain contexts.
If you add the -pedantic flag both gcc and clang will warn you about this, for example gcc says (see it live):
warning: ISO C++ forbids variable length array 'arr' [-Wvla]
int arr[size];
^
Using the -pedantic-errors flag will make this an error. You can read more about extensions in these documents Language Standards Supported by GCC and clangs Language Compatibility section.
Update
The draft C++ standard covers what is a integral constant expression in section 5.19 Constant expressions paragraph 3 and says:
An integral constant expression is an expression of integral or unscoped enumeration type, implicitly converted to a prvalue, where the converted expression is a core constant expression. [...]
It is not intuitively obvious from reading this what all the possibilities are but Boost's Coding Guidelines for Integral Constant Expressions does a great job of that .
In this case since you are initializing size with a literal using const would suffice to make it an integral constant expression (see [expr.const]p2.9.1) and also bring the code back to being standard C++:
const int size = 10;
using constexpr would work too:
constexpr int size = 10;
It would probably help to read Difference between constexpr and const.
For reference the equivalent section to 8.3.4 paragraph 1 in the C99 draft standard would be section 6.7.5.2 Array declarators paragraph 4 which says (emphasis mine):
If the size is not present, the array type is an incomplete type. If the size is * instead of being an expression, the array type is a variable length array type of unspecified size, which can only be used in declarations with function prototype scope;124) such arrays are nonetheless complete types. If the size is an integer constant expression and the element type has a known constant size, the array type is not a variable length array type; otherwise, the array type is a variable length array type.
Why this type of declaration
int nArraySize = 7;
char szName[nArraySize] = "Mollie";
returns this error:
error: variable-sized object 'szName' may not be initialized
but when I declare the 'arraySize' variable as a 'const int' it works ?
const int nArraySize = 7;
char szName[nArraySize] = "Mollie";
It must be said first that in C++ language, the size part of array declaration is required to be an Integral Constant Expression (ICE). A const int object declared with an initializer can be used in an ICE. An int object cannot be used in an ICE. That's the formal part of it.
However, judging by the error message, your C++ compiler supports C99-style variable-length arrays (VLA) in C++, as a non-standard extension. That means that in your compiler you are allowed to use non-constant expressions to specify size in array declarations. Yet even if VLAs themselves are supported, such arrays still cannot be initialized. This is prohibited by the specification of VLAs in C99, and that is exactly how their specification is "inherited" by your C++ compiler.
In other words, contrary to what other answers stated, this code will probably be accepted by your C++ compiler
int nArraySize = 7;
char szName[nArraySize];
even though it is formally illegal C++. It is the = "Mollie" part that triggers the error.
Because C++ does not support variable-length arrays (introduced in the C-99 standard, but not in any version of C++). When you declare nArraySize as a non const int, the compiler complains because nArraySize may change at runtime. If nArraySize is const, the compiler knows that it cannot change at runtime, and therefore the array size of szName cannot be variable (i.e. can be deduced at compile time). In C++ (and versions of C before C99), the size of an array must be a constant that can be deduced at compile-time.
Because the program has to know at compile time how much memory to alocate for your variables. When you don't make your nArraySize constant it is assumed it may change during runtime. While making it constant asures the compiler this value will not be changed.
The first is a variable length array and it's standardized in C (since the C99 standard) but not in C++.
C++ needs all arrays to have their sizes available at compile time, not runtime. Declaring the size as a constant makes it a compile-time constant.
The Standard does not permit dynamically-sized, statically-allocated arrays. You may find that in GCC you will be able to do this, but that is because that is one of a number of extensions that allow non-conforming behavior.
An array is defined like this:
D1 [ constant-expressionopt] attribute-specifier-seqopt
Where the size is an integral constant expression. The Standard defines an integral constant expression like this:
An integral constant expression is an expression of integral or unscoped enumeration type, implicitly converted to a prvalue, where the converted expression is a core constant expression. [Note: Such expressions
may be used as array bounds (8.3.4, 5.3.4), [...] — end note]
int n = 10;
int x[n]; // error!
The variable n is not a constant expression, so it will not work for this case.
By adding constexpr (C++11) to the type, it will be useable in a constant expression. But in this case const is enough to make it work:
int const n = 5;
int x[n];
On the other hand, dynamic arrays take a dynamic size specifier:
int n = 10;
int *x = new int[n];
But an option I would recommend using is std::vector which is a wrapper around a dynamically-sized buffer:
#include <vector>
int main()
{
int n = 10;
std::vector<int> x(n); // x.size() is 10
}
I hope this helped.
#include<iostream>
using namespace std;
const int a[]={1,2,3,4,5};
int b[a[2]];
int main(){return 0;}
Why the compilation error is coming with this code. Could someone explain in brief, concise and exact behavior.
I am getting with gcc 4.6
tt.cc:5:11: error: array bound is not an integer constant before ']' token
I find the error message quite explanatory. You cannot declare
int b[a[2]];
you should use
int b[3];
And you very probably want to use std::vector instead.
C++11 tricks
you could compile your code with a C++11 compiler (like just released GCC 4.7) by declaring
constexpr int a[]={1,2,3,4,5};
but that seems insane to me. You really want to use vectors or some other container type.
For C++03:
8.3.4.1 An array declaration is:
D1[constant-expression]
constant-expression is, in this case, an integral constant expression.
If we look at 5.19.1 we find what an integral constant expression is:
literals
const variables
static data members of integral or enumeration types initialized with constant expressions
non-type template parameters of integral or enumeration types
sizeof expressions
floating literals, if cast to integral or enumeration types
In particular, except for sizeof, the following can not be used:
functions
class objects
pointers or references
assignment, increment, decrement, function-call or comma operators
In your case, a[2] is neither, so it can't be used as the array's length.
For C++11:
Roughly the same, only that you can declare a as a constexpr and use it as the array's length. However, this coding style is generally bad, and you shouldn't write stuff like that unless you've evaluated all other options.
EDIT:
I don't see why the question was downvoted. I find it very interesting:
1) You can actually do this in C++11, with constexpr
2) Even in C++03, the behavior is at least strange (if you don't search the standard):
const int x = 2;
const int a[]={1,2,3,4,5};
int c[x]; //compiles
int b[a[2]]; //doesn't
int b[a[2]];
You need to to provide a compile-time available array size (that is, a fixed size array), or allocate dynamically using malloc etc.