Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

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

like image 753
speeder Avatar asked Mar 20 '23 23:03

speeder


2 Answers

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.

like image 194
Keith Thompson Avatar answered Apr 01 '23 10:04

Keith Thompson


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.

like image 21
AnT Avatar answered Apr 01 '23 09:04

AnT