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

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.

Related

C constant expressions. const variables as constant expressions

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".

Why does C not support initializing const variables using an if-else statement?

I want to initialize a const variable using an if-else. For example:
const int foo;
if (bar) {
foo = 1;
} else {
foo = 2;
}
In Java, this is legal code (using final instead of const). The reason is that in all possible outcomes, the variable is assigned once and never reassigned. In C, this is not legal code. What is the reason that it can't be legal code in C?
You can initialize the foo variable conditionally by means of the ternary operator:
const int foo = bar ? 1 : 2;
Note that, if foo is not an automatic variable, then the initializing expression must be abe to be evaluated at compile-time, otherwise it won't compile.
You can use the ternary operator, but keep in mind that for objects with static or thread-local storage class, the initializer expression needs to be a compile-time constant:
const int bar = 42;
#define BAR 42
#if 0
const int foo = bar ? 1 : 2; /*ERROR -- bar is not an integer constant */
#else
const int foo = BAR ? 1 : 2;
#endif
void fn(void)
{
const int foo = bar ? 1 : 2;
#if 0
static const int stc_foo = bar ? 1 : 2; /*ERROR*/
#else
static const int stc_foo = BAR ? 1 : 2;
#endif
}
The reason an if-else statement can't be used for the initialization is because allowing it would require some rather extensive changes to the C grammar, and it would possibly make the C grammar and semantics much more complicated.
Basically, instead of simply having to verify that the declarator is followed by = and an initializer expression and that that initializer expression is a constant, the compiler would have to remember each static/thread-local variable that hasn't been initialized and then look for unconditionally executed compile-time evaluable branches that follow it an assign to it and use them for the initialization.
Furthermore, statements would have to be allowed in file scope (statements are not allowed in file scope in the current C grammar) and verified for constness and memory access limited to writes to translation-unit-local global variables. Alternatively, they could be implicitly turned into global constructors, but that would introduce additional problems such as constructor ordering between compilation units (which would be hard to resolve if the constructor generation were implicit), the need for implementations to support global constructor in the first place, or the blurring of the currently rather straightforward performance characteristics of static variable assignments.
In C, const makes the variable read-only.
You can initialize const variable only at the time of declaration, not after that as it becomes read-only.
That's why your code is not legal in C, as you are updating a read-only variable.
Hope it will help !!

Initializing global variable works with integer literal but not with const type variable [duplicate]

This question already has answers here:
Error "initializer element is not constant" when trying to initialize variable with const
(8 answers)
Closed 7 years ago.
I'm working through some openGL tutorials and since they all have C++ syntax I need to convert them to C syntax and I have some problems with global variables.
So I have my extern declarations in the shared header LUtil.h
#ifndef LUTIL_H
#define LUTIL_H
#include "LOpenGL.h"
#include <stdio.h>
#include <stdbool.h>
//Color modes
extern const int COLOR_MODE_CYAN;
extern const int COLOR_MODE_MULTI;
//Screen constants
extern const int SCREEN_WIDTH;
extern const int SCREEN_HEIGHT;
extern const int SCREEN_FPS;
extern int gColorMode;
extern GLfloat gProjectionScale;
...
And I have my LUtil.c file in which the declaration happens
#include "LUtil.h"
//The current color rendering mode
const int COLOR_MODE_CYAN = 0;
const int COLOR_MODE_MULTI = 1;
//constants
const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;
const int SCREEN_FPS = 60;
//The projection scale
int gColorMode = 0;
GLfloat gProjectionScale = 1.f;
...
Now if I compile like this it works. But if I initialize the gColorMode constant like this in LUtil.c
int gColorMode = COLOR_MODE_CYAN;
I get a compiler error saying that my initializer is not constant despite having declared COLOR_MODE_CYAN a const and initializing with it.
Why is this?
In C, a variable declared const is not a constant, it's called const-qualified variable. It is not considered a compile time constant expression.
You need to either use an integer constant or a #define to get your work done.
FWIW, a variable with const is a real constant (integral constant expression) in case of C++.
const in C doesn't actually create a "constant". You still end up with a variable (reserved memory) but the compiler just forbids writes to that variable.
Even though it is marked const, you could cast it back to non-const and modify it (please don't!) Because of this (and possibly other reasons), it requires emitting a memory read to access the value of your const int constants, which is not allowed when initializing another variable (it must be a compile-time constant.)
Instead, use a #define in your header file.
const int COLOR_MODE_CYAN
is still a variable even if it is constant(which just means that compiler will throw a compile time error if you try to modify it using code). Which means it will have a memory location and a value, when the program will be loaded into memory. Its value can be calculated by the = operator. And all the operators should be placed inside a function definition because they can be resolved only at run time.
And since the variable which you are assigning the value to is a global variable. Its initial value must be declared at compile time so that the compiler can put it into an appropriate segment(.bss or .data). But the assigned value is a variable, and in order to find out its value = operator has to be executed. Which can not be done at compile time. So that compiler is throwing an error that GIVE ME A CONSTANT VALUE THAT I CAN ASSIGN TO THIS GLOBAL VARIABLE LIKE 3, 4, 5.

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));
}

declaring variables of storage classes

As I was coding, I was declaring the following:
const int a = 4;
Is "a" a variable that the compiler won't let me change? or would I need static const int a = 4?
Thanks!
When you define a with const int a = 4;, you should not change a, but the compiler is not required to prevent you from doing so. The compiler should produce a diagnostic message if you attempt to modify a directly, as with a = 5;, but there are other ways you could try to modify a that the compiler will not necessarily catch, such as * (int *) &a = 5;. If you do this, the C standard does not define the resulting behavior. Adding static to the definition will not change this.
const int a = 4;
Is a variable that the compiler won't let you change.
The static qualifier changes the way the variable behaves. Specifically what it means is that the variable is "allocated" when the program starts execution and only deallocated when the program terminates. In particular static class variables maintain their state independent of instantiated objects. Static variables in functions maintain their state between function calls. Static qualifiers on global constants are redundant in this case and are only used to determine internal/external linkage.
You cannot modify a when you have declared it as const.
const int a = 5;
a = 6;
Does not compile (expression must a modifiable lvalue).

Resources