const int i = 100;
int *j = &i;
int array[i] = {0};
Is this legal? I read somewhere that without the &i, i and 100 would be added to the symbol-table, but because of the &i, storage is forced for i, and 100 would be stored in i at compile time; therefore the compiler would not be able to read the value of i (from storage) to allocate the array - is this true?
In C90 and C99, the definition int array[i] = {0}; is not legal (regardless of what you do with i).
An array declared at file scope has to have an integer constant expression as length (6.5.4.2 or 6.7.5.2, §2, resp.)
If the definition has function/block scope it is still illegal because
in C90, variable length arrays are not allowed (6.5.4.2)
in C99, variable length arrays must not be initialized (6.7.8, §3)
int *j = &i; should be const int *j = &i;. After that, your code is legal.
EDIT: in C++
There's absolutely no reason why a variable couldn't have both a compile-time constant value and physical storage. Thus it depends on the standards of the language.
C++ allows a constant integer to be a compile-time constant. C does not. Some C++ compilers which also compile C will mix and match some features of both languages so your compiler might accept it as an extension to the standard.
Is this legal? I read somewhere that without the &i, i and 100 would be added to the symbol-table, but because of the &i, storage is forced for i, and 100 would be stored in i at compile time; therefore the compiler would not be able to read the value of i (from storage) to allocate the array - is this true?
Here's the language from the C99 standard:
6.7.3 Type qualifiers
...
3 The properties associated with qualified types are meaningful only for expressions that
are lvalues.114)
114) The implementation may place a const object that is not volatile in a read-only region of
storage. Moreover, the implementation need not allocate storage for such an object if its address is
never used.
So, sort of true, but not really relevant in this case. The compiler doesn't have to create storage for i to use its value elsewhere; it may simply use an immediate operand with the value of 100 in place of i. Either way, the line int j = &i; is unnecessary.
i is not an integer constant expression, meaning it's not a compile-time constant (in C; C++ is different in this regard); it's a run-time variable whose value may not be modified during its lifetime.
As of C99, you can specify array sizes using a run-time variable, even one declared const, so
const int i = 100;
int array[i];
will allocate array as a 100-element array of int. However, you cannot use an initializer with a VLA, so int array[i] = {0}; is not valid.
Again, C++ is different, and doesn't support VLAs at all. But since i is declared const, C++ treats it as a compile-time constant, meaning your code should be legal C++ (it builds for me, anyway).
More from C99:
6.7.5.2 Array declarators
...
4 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.
...
6.7.8 Initialization
...
3 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.
124) Thus, * can be used only in function declarations that are not definitions (see 6.7.5.3).
Related
I understand that the size of the built-in array must be a constant expression:
// Code 1
constexpr int n = 5;
double arr[n];
I do not understand why the following compiles:
// Code 2
const int n = 5;
double arr[n]; // n is not a constant expression type!
Furthermore, if the compiler is smart enough to see that n is initialized with 5, then why does the following not compile:
// Code 3
int n = 5;
double arr[n]; // n is initialized with 5, so how is this different from Code 2?
P.S. This post answers using quotes from the standard, which I do not understand. I will very much appreciate an answer that uses a simpler language.
n is not a constant expression type!
There is no such thing as a constant expression type. n in that example is a expression, and it is in fact a constant expression. And that is why it can be used as the array size.
It is not necessary for a variable to be declared constexpr in order for its name to be a constant expression. What constexpr does for a variable, is the enforcement of compile time constness. Examples:
int a = 42;
Even though 42 is a consant expression, a is not; Its value may change at runtime.
const int b = 42;
b is a constant expression. Its value is known at compile time
const int c = rand();
rand() is not a constant expression, and so c is neither. Its value is determined at runtime, but may not change after initialisation.
constexpr int d = 42;
d is a constant expression, just like b.
constexpr int f = rand();
Does not compile, because constexpr variables must be initialised with a constant expression.
then why does the following not compile:
Because the rules of the language don't allow it. The value of n is not compile time constant. The value of a non-const variable can change at runtime.
The language cannot have a rule that some value doesn't change at runtime, then it is a constant expression. That would not be of any use to the programmer since they cannot assume which compiler will be able to prove the constness of which variable.
The language has to exactly specify the cases where an expression is constant. It would also be infeasible to specify that a non-const variable is a constant expression if it hasn't been modified before its use, because it is impossible to prove in most cases, even though you've found one case where the proof happens to be easy.
// n is not a constant expression type!
But it is. Per [expr.const]/3
A variable is usable in constant expressions after its initializing declaration is encountered if it is a constexpr variable, or it is a constant-initialized variable of reference type or of const-qualified integral or enumeration type. An object or reference is usable in constant expressions if it is [...]
a complete temporary object of non-volatile const-qualified integral or enumeration type that is initialized with a constant expression.
So, if you have a const integer intialized with a constant expression then you still have a constant expression as nothing can change. This is a rule that existed before constexpr was ever a thing as it allowed programmers to initialize arrays with constant variables instead of using macros.
Furthermore, if the compiler is smart enough to see that n is initialized with 5, then why does the following not compile:
Because the integer is not const so it could be changed. Even though in your case you can prove it can't change, in general you can't so it is just not allowed.
A value declared constexpr means that this value does not change and is known during compile time.
A value declared const means that this value does not change after initialization, but it is not mandatory to be known during compile time. In other words, a constexpr is const, but a const is not constexpr.
Your "Code 3" example doesn't work because you need a constant known at compile time in order to allocate memory for a vector, so you need a constexpr.
When I was reading about the array initialization in this tutorial. I found out this note.
type name [elements];
NOTE: The elements field within square brackets [], representing the number of elements in the array, must be a constant expression, since arrays are blocks of static memory whose size must be determined at compile time, before the program runs.*
As I know array allocate the memory in the run time. This should be a false note? or what does it means?
Please check if the following answers help in giving you clarity about this.
Static array vs. dynamic array in C++
Static arrays are created on the stack, and necessarily have a fixed size (the size of the stack needs to be known going into a function):
int foo[10];
Dynamic arrays are created on the heap. They can have any size, but you need to allocate and free them yourself since they're not part of the stack frame:
int* foo = new int[10];
delete[] foo;
You don't need to deal with the memory management of a static array, but they get destroyed when the function they're in ends
Array size at run time without dynamic allocation is allowed?
C99 standard (http://en.wikipedia.org/wiki/C99) supports variable sized arrays on the stack. Some of the compilers might implement these standards and support variable sized arrays.
The declaration T a[N] requires that N be a converted constant expression.
Converted constant expression is an expression implicitly converted to
prvalue of type T, where the converted expression is a core constant
expression. If the literal constant expression has class type, it is
contextually implicitly converted to the expected integral or unscoped
enumeration type with a constexpr user-defined conversion function.
An int literal such as 5 is a prvalue, so can be used in the declaration T a[5], but an lvalue, for example int n = 5 cannot be used in the declaration T a[n], unless the lvalue under-goes an implicit lvalue-to-rvalue conversion where the lvalue:
a) has integral or enumeration type, is non-volatile const, and is
initialized with a constant expression, or an array of such (including
a string literal)
b) has literal type and refers to a non-volatile object defined with constexpr or to its non-mutable subobject
c) has literal type and refers to a non-volatile temporary, initialized with a constant expression
Therefore the following are valid:
const int n = 5;
int a[n];
constexpr int n = 5;
int a[n];
You may use :
int array[42];
but not
int n;
std::cin >> n;
int array[n]; // Not standard C++
the later is supported as extension by some compiler as VLA (Variable length array)
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.
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.
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?