declaring variables of storage classes - c

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

Related

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 !!

Why const variable need not to be initialized in C?

const variables in C++ must be initialized means uninitialized const variable isn't possible & it is a compiler error. But why it is not same in C language also?
Consider following program that compiles fine C:
#include <stdio.h>
int main()
{
const int a;
}
What is the reason to allow uninitialized const? Wouldn't it be nice If C also follows same rule as C++ does? Is it due to performance concerns that local const variable needs to be initialized every time when a function is called & initialization takes time?
The difference probably stems, among other things, from a significantly more relaxed approach to initialization in C language in general, not only with regard to const objects. For example, this code is illegal in C++
goto over;
int a = 5;
over:;
because it jumps into scope of a bypassing its initialization. Meanwhile in C this code is perfectly legal, with variable a having indeterminate value at over:.
The same logic applies to your const int a declaration. C language simply believes that an uninitialized object is not a big deal, even in situations where it is no longer possible to set it to a determinate value later.
The primary reason for stricter initialization requirements in C++ is introduction of non-trivial initialization (constructors) into the language, i.e. initialization that cannot be meaningfully bypassed. Scalar objects and their initialization in C++ just tagged along as small part of a much broader concept.
Wouldn't it be nice If C also follows same rule as C++ does?
I don't see it. C and C++ are substantially different languages. And they treat const quite differently as well.
Why const variable need not to be initialized in C?
History.
const was specified in C++ from its beginning and the use met that language's goals. const was later specified in C with a related but different meaning to minimize exiting C code compatibility issues.
Since C began without const, its later inclusion is more like a read-only modifier than a constant one. This allowed existing compilers to essential treat const as nothing for writing to a const is undefined behavior. Newer compilers/code could take advantage that const provides.
const int a;
a = 5; // problem in C as code attempts to write `a`
// Really should be `const char *fred`, but allowed for backwards compatibility.
char *fred = "sally";
C++ took a stronger approach and demands the initialization.
See also const in C vs const in C++
Because C is absolutely confident in the programmer and does allow him to do a lot of things including stupid ones : int *x = NULL; x[4] = 12; will be compiled without error and even without warnings by many compilers.
More precisely, const is just a promise that programmer does that the variable should not be modified, and that compiler could considere it as constant if is can help optimizations. But compiler will never enforce any run time rules to forbid to change a const value :
const a = 1;
int *ix = (int *) &a;
*ix = 2;
printf("a=%d\n", a); /* UB : could print 1 or 2 */
will be compiled without a warning. But it will invoke undefined behaviour because you modified an object declared as const.
I believe that not initializing const variables is allowed simply because current C specification does not forbid it ! In former versions, initialization has always be optional. Maybe future versions could force initialization for automatic variables
Anyway a global or static const variable is in fact automatically initialized (per C language specification 6.7.9 10) : If an object that has static or thread storage duration is not initialized
explicitly, then: ... if it has arithmetic type, it is initialized to (positive or unsigned) zero; ...
So static const a; is perfectly valid as is const a if a is global and in those case a=0.
I believe the reason is convention.
In C, virtually no kind of object is ever required to be initialized, and has never been required to be initialized.
const objects are just one more kind of object with one special characteristic, why make an exception for them?

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.

Usage wise difference between const & volatile qualifier in C?

I have already gone through the answer of question #
What are the differences between const and volatile pointer in C?
I understand the explanation that:
The const modifier means that this code cannot change the value of the variable, but that does not mean that the value cannot be changed by means outside this code.
However, volatile says "this data might be changed by someone else" and so the compiler will not make any assumptions about that data.
Which implies that both type of variables can be changed by external event.
But,then where is the difference in usage of const & volatile?
In C, does compiler optimizations work for const?
volatile and const are different in many ways, they are two distinctively different features.
Declaring a variable just as const never means "I expect this variable to be modified outside the program", I'm not sure where you got that idea from. If you expect a const variable to be modified outside the code, it must be declared as volatile const or the compiler may assume that the variable is never changed.
By default, plain const variables are just like any kind of variable, they simply can't be modified by the program itself.
Just as for plain variables, const variable behavior depends a lot on in which scope they are declared. Most often they are declared at file scope and then they behave as other variables with static storage duration, except they are (likely) saved at a different part of the memory. If they are declared at local scope, they may change from time to time when the function where they reside is called.
So there are plenty of cases where const variables may be optimized. One common optimization is "string pools", where the compiler checks if the same constant string literal appears twice in the code, and then uses the same address for them. Had you expected such strings to be changed from an external source, but didn't declare them as volatile, you'd get strange bugs.
As for volatile variables, they may be modified by external sources, but they may also be modified by the program, unlike const variables.
Objects with const-qualified type are objects like other objects that you may declare in your program, only that you don't have the right to modify them. The underlying object may change for example by aliasing and the compiler has to take care, as for all other objects if such events could have happend. For example
void toto(double const* pi, double* x) {
printf("%g %g\n", *pi, *x);
printf("%g %g\n", *pi, *x);
*x = 5.0;
printf("%g %g\n", *pi, *x);
}
Here it is perfectly ok to call toto with something like toto(&a, &a) and so inside the function pi and x point to the same memory. For the second printf the compiler can assume since it did no store in the mean time that the values of *pi and *x have not changed. But for the third printf it cannot foresee if *pi has changed so it has to reload the value from memory.
volatile is different from that.
void tutu(double volatile* pi, double* x) {
printf("%g %g\n", *pi, *x);
printf("%g %g\n", *pi, *x);
}
Here, for the second printf as before the compiler can assume that *x hasn't changed, but for *pi it must assume that it could have and must reload it from memory. Use cases for volatile are much rarer in daily programmers life, they mainly concern objects that
might represent some hardware address
that could change in an interrupt handler
under some setjmp/longjmp mechanism
or by a different thread
'const' tells the compiler that the value is never changed, not by the program and not by someone else. When something is const, the compiler will optimize the code accordingly, and will usually replace the variable with constants in code. So even if it changes outside, the program may never know.
'volatile', on the contrary tells the compiler that the variable can be changed from the outside anytime, and then the compiler will not perform such optimizations, as putting the var in a register, but will always read it from memory, in case it changed.
Example for demonstrating const
function1()
{
int i = 10;
function2(&i);
}
function2(int const *ptr) // usage of const
{
*ptr = 20; //will cause error; outside function can't be modify the value of i
}
Example for volatile
function1()
{
while(1)
{
i = 20;
print(i);
}
}
function2()
{
i = 20;
while(1)
{
print(i);
}
}
consider these two function. both are seem to be same. for optimisation compiler convert function1 to function2. problem is that if value of i changed by another thread then two function become different, here while loop print value of i and another module change the value of i. so we will not get the value of i as 20 always.
volatile is used to inform the compiler not to optimise the variable.
The const modifier means that this code cannot change the value of the
variable, but that does not mean that the value cannot be changed by
means outside this code.
There are two different ways to apply the const qualifier.
A const-qualified object must not be modified by the program or the program has undefined behavior. A const volatile object can be modified by the OS/hardware/whatever, but not assigned to by the program. For the avoidance of doubt, a const object is one whose definition uses a const type for it.
A pointer-to-const-qualified-type prevents (at compile time) modifications via that pointer, but other pointers to the same object can be used to modify it. Behavior is defined provided the object itself is not const. However, the compiler may still assume that only the program modifies the object, accounting for arbitrary modifications by the OS/hardware/whatever require volatile.
A pointer-to-non-const-qualified-type is exactly the same as the pointer-to-const as far as modifications via other pointers are concerned.
However, volatile says "this data might be changed by something other than code in this program" and so the compiler will not make any assumptions about that data when optimizing.
So the differences are these:
#include <stdio.h>
void some_other_function(const int *);
int main() {
int a = 0;
int volatile b = 0;
int const c = 0;
int const *constptr = &a;
int *ptr = (int*) constptr;
printf("%d\n", a); // compiler can assume a == 0 at this point, and
// replace the code with puts("0") if it wants to
printf("%d\n", b); // compiler cannot assume a value for b, it's volatile[*]
some_other_function(constptr); // defined in another TU
printf("%d\n", a); // compiler can *no longer* assume that a == 0,
// it might have changed
*ptr = 1; // there's another example of a changing, legally
some_other_function(&c);
printf("%d\n", c); // compiler can assume c == 0 because c is const
}
[*] Although I say it "can't assume a value", it may be that some hypothetical C implementation happens to know that there is no OS or hardware mechanism to modify an automatic variable by any means that would require volatile to detect. Especially in this case, where no reference to b has escaped the function. If so, then you might find that the implementation actually can ignore volatile in this particular code, but maybe it treats extern global volatile variables "properly" because it knows that the linker provides a means to map them to the addresses of I/O ports or whatever.
The difference between Volatile and Const can be easily see in bellow case,
1) If you say some variable as Const, it may not be possible to modify by your program.
2) if you say volatile, it is just giving a hint to the compiler not to optimize the code, because the value may be changed from the external threads or other programs.
3) if we define a variable as Const Volatile, that means this variable can not be modified by same program, will not be optimized by compiler and can be modified by external threads or programs.
example:
if i write a function like below,
const freq = 10;
calfreq()
{
return (Const freq * 2);
}
here in this case compiler may optimize the code to
return(20);
all the time.
But here in m y case, freq value may change, because of external hardware / threads / programs
So, if i say Const Volatile, then problem will be fixed.

Should useless type qualifiers on return types be used, for clarity?

Our static analysis tool complains about a "useless type qualifier on return type" when we have prototypes in header files such as:
const int foo();
We defined it this way because the function is returning a constant that will never change, thinking that the API seemed clearer with const in place.
I feel like this is similar to explicitly initializing global variables to zero for clarity, even though the C standard already states that all globals will be initialized to zero if not explicitly initialized. At the end of the day, it really doesn't matter. (But the static analysis tool doesn't complain about that.)
My question is, is there any reason that this could cause a problem? Should we ignore the errors generated by the tool, or should we placate the tool at the possible cost of a less clear and consistent API? (It returns other const char* constants that the tool doesn't have a problem with.)
It's usually better for your code to describe as accurately as possible what's going on. You're getting this warning because the const in const int foo(); is basically meaningless. The API only seems clearer if you don't know what the const keyword means. Don't overload meaning like that; static is bad enough as it is, and there's no reason to add the potential for more confusion.
const char * means something different than const int does, which is why your tool doesn't complain about it. The former is a pointer to a constant string, meaning any code calling the function returning that type shouldn't try to modify the contents of the string (it might be in ROM for example). In the latter case, the system has no way to enforce that you not make changes to the returned int, so the qualifier is meaningless. A closer parallel to the return types would be:
const int foo();
char * const foo2();
which will both cause your static analysis to give the warning - adding a const qualifier to a return value is a meaningless operation. It only makes sense when you have a a reference parameter (or return type), like your const char * example.
In fact, I just made a little test program, and GCC even explicitly warns about this problem:
test.c:6: warning: type qualifiers ignored on function return type
So it's not just your static analysis program that's complaining.
You can use a different technique to illustrate your intent without making the tools unhappy.
#define CONST_RETURN
CONST_RETURN int foo();
You don't have a problem with const char * because that's declaring a pointer to constant chars, not a constant pointer.
Ignoring the const for now, foo() returns a value. You can do
int x = foo();
and assign the value returned by foo() to the variable x, in much the same way you can do
int x = 42;
to assign the value 42 to variable x.
But you cannot change the 42 ... or the value returned by foo(). Saying that the value returned from foo() cannot be changed, by applying the const keyword to the type of foo() accomplishes nothing.
Values cannot be const (or restrict, or volatile). Only objects can have type qualifiers.
Contrast with
const char *foo();
In this case, foo() returns a pointer to an object. The object pointed to by the value returned can be qualified const.
The int is returned by copy. It may be a copy of a const, but when it is assigned to something else, that something by virtue of the fact that it was assignable, cannot by definition be a const.
The keyword const has specific semantics within the language, whereas here you are misusing it as essentially a comment. Rather than adding clarity, it rather suggests a misunderstanding of the language semantics.
const int foo() is very different from const char* foo(). const char* foo() returns an array (usually a string) whose content is not allowed to change. Think about the difference between:
const char* a = "Hello World";
and
const int b = 1;
a is still a variable and can be assigned to other strings that can't change whereas b is not a variable. So
const char* foo();
const char* a = "Hello World\n";
a = foo();
is allowed but
const int bar();
const int b = 0;
b = bar();
is not allowed, even with the const declaration of bar().
Yes. I would advise writing code "explicitly", because it makes it clear to anyone (including yourself) when reading the code what you meant. You are writing code for other programmers to read, not to please the whims of the compiler and static analysis tools!
(However, you do have to be careful that any such "unnecessary code" does not cause different code to be generated!)
Some examples of explicit coding improving readability/maintainability:
I place brackets around portions of arithmetic expressions to explicitly specify what I want to happen. This makes it clear to any reader what I meant, and saves me having to worry about (or make ay mistakes with) precedence rules:
int a = b + c * d / e + f; // Hard to read- need to know precedence
int a = b + ((c * d) / e) + f; // Easy to read- clear explicit calculations
In C++, if you override a virtual function, then in the derived class you can declare it without mentioning "virtual" at all. Anyone reading the code can't tell that it's a virtual function, which can be disastrously misleading! However you can safely use the virtual keyword: virtual int MyFunc() and this makes it clear to anyone reading your class header that this method is virtual. (This "C++ syntax bug" is fixed in C# by requiring the use of the "override" keyword in this case - more proof if anyone needed it that missing out the "unnecessary virtual" is a really bad idea)
These are both clear examples where adding "unnecessary" code will make the code more readable and less prone to bugs.

Resources