C constant expressions. const variables as constant expressions - c

As per 6.6.10
10 An implementation may accept other forms of constant expressions.
the implementation may consider const (or even not const as answers are focused on it - but it is not the core of the question) variables as constant expressions.
Is my logic correct? If not why?
Some examples:
//file scope
int a = 5;
const int b = 10;
int c[a];
int d[b];
IMO both meet the requirements. They can be evaluated compile time. The value is known and the static arrays have sizes known compile time.
volatile int x = 5; //cannot be considered as known compile time. ????
void foo(int a)
{
static int b[a];
}
In this example a cannot be evaluated compile time - so it cannot be used as constant expression

Constant expressions doesn't have anything to do with the const qualifier. See 6.6/6 for the definition of an integer constant expression.
6.6/10 refers to the earlier parts of the same chapter speaking of integer constant expressions, constant expressions in initializers and so on. I believe the quoted part refers to various corner case expressions such as this:
static int x;
static int y = (int)&x;
This isn't strictly conforming since &x is not regarded as a integer constant expression but as an address constant. Neither gcc nor clang accepts it. I believe 6.6/10 allows compilers to support code like the above example as an implementation-defined extension.

Building on the link from #ryyker 's comment.
In C, an expression is considered constant if it's made entirely out of compile-time constants (i.e. literals). const variables are not accepted because const doesn't necessarily mean "constant"; it just means that a variable is immutable (read-only) from the perspective of the current compilation unit.
Consider a function like this one...
int add(const int a, const int b) {
return a + b;
You could call this function by passing in compile-time constants, but you could also pass in mutable variables. If you pass in a variable, it is treated as const (read-only) for the purposes of that scope, but its value still isn't known at compile time so it isn't a "constant".

Related

Making sure a value only has to be calculated once ever in C

I have this somewhat complex value (in that it results from combining multiple other values in some mathematically non-trivial way) which however only ever needs to be calculated once in the whole program execution. I tried to make them all static const.
But the compiler complains that my inputs to my complex value must "have a constant value" - which they obviously do at compile time.
In this example, I want to compute c from a and b once and for all. But the compiler complains that, in the assignment of c, a and b must "have a constant value" - which they quite obviously do (seems to me).
void foo(void)
{
static const int a = 10, b = 2;
static const int c = a/b;
}
Variables that are declared const are not considered constants in C, so the expression a/b is not considered constant.
The simplest way to handle this is to assign the value in main at program startup, although it means the global can't be const.

If arrays in C++ require a size that is a constant expression, why can I use a variable with just const to initialize it?

I was reading about how in C++ array sizes require a constant expression, it must be evaluated at compile time.
Then I learned that things labled const are not evaluated at compile time, and const acts as a read only.
Why am I allowed to do something like this:
const int x = 5;
int myarray[x] = {};
This compiles in Visual Studio 2015. Why doesn't x have to be constexpr?
It depends on context. In the context you show the "variable" x is a compile-time constant, because the compiler can create is as such.
If, on the other hand, you have something like
void f(const int x)
{
int myarray[x] = {};
...
}
Then that won't work because x is no longer a compile-time constant, but a run-time constant.

Array definition - Expression must have a constant value

I am creating an array on stack as
static const int size = 10;
void foo() {
..
int array[size];
..
}
However, I get the compile error: "expression must have a constant value", even though size is a constant. I can use the macro
#define SIZE (10)
But I am wondering why size marked const causes compilation error.
In C language keyword const has nothing to do with constants. In C language, by definition the term "constant" refers to literal values and enum constants. This is what you have to use if you really need a constant: either use a literal value (define a macro to give your constant a name), or use a enum constant.
(Read here for more details: Shall I prefer constants over defines?)
Also, in C99 and later versions of the language it possible to use non-constant values as array sizes for local arrays. That means that your code should compile in modern C even though your size is not a constant. But you are apparently using an older compiler, so in your case
#define SIZE 10
is the right way to go.
The answer is in another stackoverflow question, HERE
it's because In C objects declared with the const modifier aren't true
constants. A better name for const would probably be readonly - what
it really means is that the compiler won't let you change it. And you
need true constants to initialize objects with static storage (I
suspect regs_to_read is global).
if you are on C99 your IDE compiler option may have a thing called variable-length array (VLA) enable it and you won't get compile error, efficiently without stressing your code though is with MALLOC or CALLOC.
static const int size = 10;
void foo() {
int* array;
array = (int *)malloc(size * sizeof(int));
}

Why const a + const b is not a const itself?

I am making a pong clone to test a SDK... It is some years that I don't C.
Anyway, I tried to do this in the const setup phase:
const int SCREEN_W = 480;
const int SCREEN_H = 480;
const int PLAYER_H_WIDTH = 50;
const int PLAYER_H_HEIGHT = 12;
const int BUFFER = 14;
const int LEFT_BUFFER = PLAYER_H_WIDTH+BUFFER;
const int RIGHT_BUFFER = SCREEN_W-LEFT_BUFFER;
except the compiler throws a error at LEFT_BUFFER and RIGHT_BUFFER lines, thus making me wonder, why?
If the answer is "because the standard says so" I still want to know why (why the standard says so?)
EDIT because of comments:
haccks noticed that those lines inside a function (like, main(){}) do compile, while on file scope they don't. I also ask, why?
Also the specific GCC (actually MingW) error is: initializer element is not constant
An initializer for an object declared with static storage duration (outside any function, or inside a function with the static keyword) must be a constant expression, or must contain only constant expressions.
This declaration:
const int PLAYER_H_WIDTH = 50;
does not make PLAYER_H_WIDTH a constant expression. The keyword const really doesn't mean "constant" (i.e., able to be evaluated at compile time); it means "read-only".
So when you declare:
const int LEFT_BUFFER = PLAYER_H_WIDTH+BUFFER;
you're trying to initialize LEFT_BUFFER with a non-constant expression.
Reference: ISO C standard, 2011 edition, section 6.7.9 paragraph 4:
All the expressions in an initializer for an object that has static or
thread storage duration shall be constant expressions or string
literals.
To illustrate the difference between const and constant, this:
const int r = rand();
is a perfectly valid declaration, as long as it's not at file scope. The object (I don't want to call it a "variable") r is const (i.e., read-only), but not constant (i.e., its value can't be determined at compile time).
That declaration cannot appear outside any function because of C's execution model, which doesn't allow user code to be executed before main is entered.
C++ does make such objects constant if the initializer is constant and of integer type; C does not. The rule in C++ requires a bit more work by the compiler; it make the determination of whether an object's name is a constant expression dependent on the the form of its initializer. I'm not sure why C hasn't adopted the C++ rule; probably the committee didn't feel it was worthwhile (remember that any new feature imposes a burden on every compiler implementer).
As a workaround, you can either use the preprocessor:
#define PLAYER_H_WIDTH 50
/* ... */
#define LEFT_BUFFER (PLAYER_H_WIDTH+BUFFER)
or you can use a slightly ugly hack with enum:
enum { PLAYER_H_WIDTH = 50 };
enum { LEFT_BUFFER = PLAYER_H_WIDTH+BUFFER };
The latter works only for constants of type int. The trick is that an enum declaration creates an enumeration type, but the enumerators are still of type int.
Your question title shows that you've been misled by some confusion. It has nothing to do with a + b or a - b. You will get the same error if you just do
const int LEFT_BUFFER = BUFFER;
without any + or -.
The problem is that none of the names you defined are "constants", meaning that they cannot form constant expressions. For example, SCREEN_W is not a constant.
In C language the term "constant" applies to literal values (like 42) and to enum constants. Variables declared as const are not "constants" in C language. You can call them "const variables" if you want, but you will never be able to use them where true constants are required.
That is just the way it is in C. That is how it has always been in C. If you want to declare a manifest constant in C language, use #define or enum. Don't attempt to use const. const has some pleasant properties (compared to #define), but the fact that const does not produce true constants in C severely limits the usability of const for declaring manifest constants.
Your original example actually demonstrates the issue. Objects with static storage duration require constant initializers in C. Since your PLAYER_H_WIDTH, BUFFER etc. are not constants, you cannot use them as initializers for objects with static storage duration. And, again, the problem has nothing to do with + or -, it is present even in
const int LEFT_BUFFER = BUFFER;
declaration.
If you transfer these lines into local scope, your objects will no longer have static storage duration. They will become local variables. The aforementioned restriction does not apply to local variables, which is why your declarations will compile without any problems. However, if you add the keyword static to the above declarations, the error will reappear even in local scope.
C's constant is not actually a "constant" as we expected. It is an indicator to the compiler that the variable's content could not be changed. The initialize needs to be constant expressions or string literals.
The closest thing to a constant in C is using enum or #define, e.g.
enum {
SCREEN_W = 480,
SCREEN_H = 480,
PLAYER_H_WIDTH = 50,
PLAYER_H_HEIGHT = 12,
BUFFER = 14,
LEFT_BUFFER = PLAYER_H_WIDTH+BUFFER,
RIGHT_BUFFER = SCREEN_W-LEFT_BUFFER
};
I would use enum as far as I could.
use The #define directive preprocessor if you want to avoid this error !
const int SCREEN_W = 480;
const int SCREEN_H = 480;
const int PLAYER_H_WIDTH = 50;
const int PLAYER_H_HEIGHT = 12;
const int BUFFER = 14;
#define LEFT_BUFFER PLAYER_H_WIDTH + BUFFER
#define RIGHT_BUFFER SCREEN_W - LEFT_BUFFER
The #define directive is a preprocessor directive; the preprocessor replaces those macros by their body before the compiler even sees it. Think of it as an automatic search and replace of your source code.
But a constant defined with the const qualifier is best thought of as an unmodifiable variable. It has all the properties of a variable: it has a type, it has a size, it has linkage, you can take its address.
for this the compiler can't compile the assignment of const variable to another, because the assigned variable is not a constant expression.

Why can't I initialize a static variable with a non literal value?

I had this code:
int foo(void){
return 1;
}
int main(void){
static const int x = foo();
//do stuff
return 0;
}
But I got an error about initializing a static variable with a non-const value. I thought it had something to do with the const specifier, but it didn't. I ended dropping the const keyword and doing this:
int foo(void){
return 1;
}
int main(void){
static int x = 0;
if (x == 0) x = foo();
//do stuff
return 0;
}
Now, why can't the compiler just delay the initialization of the static int x variable until it's used, and more importantly, why can't it just put it in a read-write section, and just enforce that it's not written to in compile time? I'd like to use the const AND static keyword for improved semantics in my code, but I don't really care how the compiler handles this, just let it work.
Is my understanding of the C standard wrong? Or is my compiler sucking? It's MSVC 9.0.
C requires it.
From the C Standard:
(C99, 6.7.8p4) "All the expressions in an initializer for an object that has static storage duration shall be constant expressions or string literals."
Note that the const qualifier does not mean constant but rather read-only. A const object is not a constant in C.
The reason a static object cannot be initialized by a non constant value is related to the fact that the initialization of a static object is done "prior to program startup" (C99, 6.2.4p3).
The value for initialization must be determined at compile or link time. C doesn't have the concept of constructors that could be run at the startup of the program.
This constraint comes from C standard's section 6.7.8/4, so it's not just your compiler:
All the expressions in an initializer for an object that has static storage duration shall be constant expressions or string literals.
The reason for this is that unlike C++ standard, C sdoes not require execution environments to provide an entry point for pre-run initialization (while certainly not prohibiting it; The manner and timing of static initialization (5.1.2) is unspecified).

Resources