Simple question:
Why does the code below work?
int main() {
const int a = 4;
const int b = 16;
const int size = b/a;
int arr[size] = {0,1,2,3};
return 0;
}
I thought the size of static arrays have to be defined during compile time, thus only with an "int" literal. In the code above, the code compiles although the size is a calculation. Is this calculation done during compile time?
If yes, maybe my understanding of compile and running is wrong: Compile just goes through the syntax and translates the code to machine code, but does not do any calculations...
Thank you!
Is this calculation done during compile time?
Yes, compilers did a lot optimizations and calculations for you, the initialization in your code is ok even without any optimization, and it's the result of pre-calculation of compiler.
Generally, calculation here includes the constexpr, const type declaration and so on, which are already in the definition of language itself(see constant expression).
compile-time const and example
just see the output of the example.
compile-time constexpr and example
The constexpr specifier declares that it is possible to evaluate the value of the function or variable at compile time.
This is how array can be initialized, an array declaration is as below:
noptr-declarator [ expr(optional) ] attr(optional)
here, expr is:
an integral constant expression (until C++14) a converted constant expression of type std::size_t (since C++14), which evaluates to a value greater than zero
which are all constant expression, which says:
an expression that can be evaluated at compile time.
So, using the so-called pre-calculation to initialize an array is ok.
Here's also a follow-up: there're also a lot of ways to save more calculations and time by let them done during compile time, and they are shown in the link above.
Just to mention something different: As for optimizations, you can see the difference between the assembly codes of version -O0 and -O3 when calculating the sum from 1 to 100, it's a jaw-breaker -- you'll see the result 5050 is in the assembly code in the -O3 version, it's also a kind of compile-time calculation, but not enabled for all kinds of situation.
I thought the size of static arrays have to be defined during compile time, thus only with an "int" literal.
The first part is true, but the second part was only true before c++11 (you could also have done const int i = 1 + 2). From c++11, the rules say that the initializing expression is evaluated to see if it yields a constant expression.
There is also the rule that a const integral type is implicitly constexpr (i.e. computed at compile time), so long as the initializing expression is computable at compile time.
So in this expression:
const int size = b/a;
the variable size is required to be computed at compile time, so long as the expression a/b is a constant expression. Since a and b are also const ints that are initialized with a literal, the initialization of size is a constant expression. (Note that removing const from any of the declarations will make size a non-constant expression).
So size is required to be computed at compile time (the compiler has no choice in the matter, regardless of optimizations), and so it can be used as an array dimension.
Here are the complete rules for how this works.
Related
I'm using godbolt to see generated code with gcc and clang.
I tried to implement to djb2 hash.
gcc always trying is best to eval constexpr function.
clang is evaluating constexpr only if the variable is constexpr.
Let's see the example:
constexpr int djb2(char const *str)
{
int hash = 5381;
int c = 0;
while ((c = *str++))
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
return hash;
}
int main()
{
int i = djb2("hello you :)");
}
With this example, gcc is evaluating a compile time i. But clang at run time.
If I add constexpr to i, clang is evaluating also at compile time.
Do you know if the standard is saying something about that ?
EDIT: thanks to all. So, as I understand, without constexpr the compiler is doing what is want. With constexpr, the compiler is forced to evaluating the constant.
Your program has undefined behavior.
The shift hash << 5 will overflow which has undefined behavior for signed integer types before C++20.
In particular that means that calling your function can never yield a constant expression, which you can verify by adding constexpr to your declaration of i. Both compilers will then have to diagnose the undefined behavior and will tell you about it.
Give hash an unsigned type and your code will actually have well-defined behavior and the expression djb2("hello you :)" will actually be a constant expression that can be evaluated at compile-time, assuming you are using C++14 or later (The loop was not allowed in a constexpr function in C++11.).
This still doesn't require the compiler to actually do the evaluation at compile-time, but then you can force it by adding constexpr to the declaration of i.
"Force" here is relative. Because of the as-if rule and because there is no observable difference between evaluation at compile-time and runtime, the compiler is still not technically required to really do the computation only at compile-time, but it requires the compiler to check the whole calculation for validity, which is basically the same as evaluating it, so it would be unreasonable for the compiler to repeat the evaluation at runtime.
Similarly "can be evaluated at compile-time" is relative as well. Again for the same reasons as above, a compiler can still choose to do the calculations at compile-time even if it is not a constant expression as long as there wouldn't be any observable difference in behavior. This is purely a matter of optimizer quality. In your specific case the program has undefined behavior, so the compilers can choose to do what they want anyway.
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.
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.
#include <iostream>
using namespace std;
int main(void){
int size = -2;
int* p = new int[size];
cout<<p<<endl;
return 0;
}
Above code compiles without any problem on visual studio 2010.
But
#include <iostream>
using namespace std;
int main(void){
const int size = -2;
int* p = new int[size];
cout<<p<<endl;
return 0;
}
But this code(added const keyword) gives error on compilation(size of array cannot be negative).
Why these different results ?
By making it a constant expression, you've given the compiler the ability to diagnose the problem at compile time. Since you've made it const, the compiler can easily figure out that the value will necessarily be negative when you pass it to new.
With a non-constant expression, you have the same problem, but it takes more intelligence on the part of the compiler to detect it. Specifically, the compiler has to detect that the value doesn't change between the time you initialize it and the time you pass it to new. With optimization turned on, chances are pretty good at least some compilers still could/would detect the problem, because for optimization purposes they detect data flow so they'd "realize" that in this case the value remains constant, even though you haven't specified it as const.
Because const int size = -2; can be replaced at compilation time by the compiler - whereas a non-const cannot - the compiler can tell that size is negative and doesn't allow the allocation.
There's no way for the compiler to tell whether int* p = new int[size]; is legal or not, if size is not const - size can be altered in the program or by a different thread. A const can't.
You'll get into undefined behavior running the first sample anyway, it's just not legal.
In the first, size is a variable whose value happens to be -2 but the compiler doesn't track it as it is a variable (at least for diagnostic purpose, I'm pretty sure the optimizing phase can track it). Execution should be problematic (I don't know if it is guarantee to have an exception or just UB).
In the second, size is a constant and thus its value is known and verified at compile time.
The first yields a compilation error because the compiler can detect a valid syntax is being violated at compilation time.
The Second yields a Undefined Behaivor[1] because compiler cannot detect the illegal syntax at compilation time.
Rationale:
Once you make the variable a const the compiler knows that the value of the variable size is not supposed to change anytime during the execution of the program. So compiler will apply its optimization & just simply replace the const integral type size with its constant value -2 wherever it is being used at compile time, While doing so it understands that legal syntax is not being folowed[1] and complains with a error.
In the Second case not adding a const prevents the compiler from applying the optimization mentioned above, because it cannot be sure that the variable size will never change during the course of execution of the program.So it cannot detect the illegal syntax at all.However, You end up getting a Undefined Behavior at run-time.
[1]Reference:
C++03 5.3.4 New [expr.new]
noptr-new-declarator:
[ expression ] attribute-specifier-seqopt
noptr-new-declarator [ constant-expression ] attribute-specifier-seqopt
constant-expression are further explained in 5.4.3/6 and 5.4.3/7:
Every constant-expression in a noptr-new-declarator shall be an integral constant expression (5.19) and evaluate to a strictly positive value. The expression in a direct-new-declarator shall have integral or enumeration type (3.9.1) with a non-negative value. [Example: if n is a variable of type int, then new float[n][5] is well-formed (because n is the expression of a direct-new-declarator), but new float[5][n] is ill-formed (because n is not a constant-expression). If n is negative, the effect of new float[n][5] is undefined. ]
#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.