Why is it impossible to initialize a constant variable after its declaration? - c

This is not a problem, but I'd like to understand the following behaviour:
int main() {
// This does not work
int const a;
a = 50;
// This work
int const a = 50;
}
Why does the compiler throw the following error:
main.c:4:7: error: assignment of read-only variable ‘a’
I don't understand why even the initialization is forbidden. The line 3 has no translation in the assembly language. Does the compiler not detect that the line 4 is the first affectation (translation issue: I meant "assignment")?
EDIT: Ok, so let's say this is how the C language is. But this can be a problem because when I use C89, the declarations must be at the top of the functions and I can't use constant variables because assignments must be placed after the declarations . The only solution is to declare non-const variables or initialize all the variables. I find this "dirty".

I don't understand why even the initialization is forbidden.
The key is in the error message:
error: assignment of read-only variable ‘a’
It isn't an initialization, it is an assignment. An assignment modifies an existing object, and that is not permitted if said object is const.
This, on the other hand, despite using the = syntax, is an initialization:
int const a = 50;

Because it's a constant!
a = 50; is an assignment, not an initialisation.
int const a; essentially sets a to an indeterminant value (which you should never read by the way). Perhaps your compiler will warn you of this if you ask it nicely.

" Does the compiler not detect that the line 4 is the first affectation?"
It could, in this simple case. However the rules are written to cover all cases of initialization.
Compiler looks at one file at a time. A program may be composed of many files. The language allows variables to be declared in one translation unit (file) and used in another.
The rules are written so they take care of all such cases

Related

why was my version of ungetch() and getch() wrong? [duplicate]

Suppose I have a global variable, and I want to assign another variable to it. I've found out that you can assign another value to a global variable inside a function:
int i = 8;
int main(void)
{
i = 9; /* Modifies i */
return 0;
}
However, assignment of the global variable outside of a function does not work!
int i = 8;
i = 9; /* Compiler error */
int main(void)
{
return 0;
}
I get the following error message:
warning: data definition has no type or storage class
warning: type defaults to 'int' in declaration of 'i'
error: redefinition of 'i'
note: previous definition of 'i' was here
int i = 8;
^
Why is this happening?
This is a definition of a global variable, with the optional initialisation to a specific value:
int i = 8;
Note that it is not code which gets ever executed, the variable will just be set up to initially contain the 8. Either consider it "magic" (a helpful model for many things not really defined by the standard) or think of tables with values being copied to memory locations before any code is executed.
This is a piece of code which has no "frame" in which it is executed.
(Or you intend it to be. The compiler is of other opinion, see below.)
i = 9;
There is no function containing it. It is not clear when it should be executed. That is what the compiler does not like.
In C, all code has to be inside a function and will only be executed if that function is called, e.g. from main().
Other language, mostly those which execute "scripts" by interpreting them (instead of code being turned into executeables, e.g. by a compiler) allow to have code anywhere. C is different.
The compiler sees this differently:
i = 9;
it is not inside a function, so it cannot be code
it looks like a variable definition, assuming that you mean it to be an int, i.e. the default
but relying on defaults is not a good idea, so warn about missing type and that the default is used
also, if it is a definition, then it is the second one for i, now that is really wrong, so show an error and fail the compiling
just to be helpful, mention where the first definition of i is
That is how to read the compiler output you have quoted.

Pass by address of value in C

I know the following is an example of pass by reference in C++, input is passed as a reference:
void add(int &input){
++input;
}
I also know pass by reference is not available in C. My question is, does the above syntax mean something else in C (i.e pass by value or something), or is it meaningless?
Trying to compile it in C gives this error:
error: parameter name omitted
does the above syntax mean something else in C?
No, it does not. It's not valid C at all.
The & operator means two things in C. The binary one is bitwise "and", and the unary is "address of". You cannot use it in declarations.
C++ chose this for reference variable for two reasons. The first is that since it is not valid C, it will not collide with existing C code. When C++ came, they focused pretty hard on making C++ backwards compatible with C. In later versions of C++, the backwards compability with C is not a very high priority. To a large degree, this is because C++ was a fork of a pretty early version of C, and since then both languages have evolved somewhat independently. For instance C99 added (but it was removed later) variable length arrays, which were never added to C++. Another example is designated initializers.
The other reason is that the meaning of the operator is pretty similar. You can interpret it as "instead of forcing the caller to send the address, I will take the address of whatever he is sending". They simply just moved the & to the function prototype instead of the function call.
And yes, there are a few other differences between pointers and references too
A reference must be initialized. (Assigned upon declaration)
A reference cannot be reassigned to "point" to another object.
A reference must always "point" at an object. It cannot be NULL.
There is one danger with references. In C, you can be certain that a function will never change the variables you send as arguments to a function unless you're sending the address to them. This C code:
int main(void)
{
int a = 42;
foo(a);
printf("%d\n", a);
}
will ALWAYS print "42", no matter how the function foo is defined. Provided that the code compiles and there's no weird undefined behavior. In C++, you don't have that guarantee.
No, it is simply invalid syntax in C.
That is actually one of the reasons that C++ picked this syntax for the feature: it wouldn't change the meaning of any existing C code.
While C does not have pass by reference (and the code will produce compile error), you can get something closer by following the rules:
In the prototype, replace & with * const (reference cannot be reassigned).
In the body, replace reference to varname with (*varname)
When calling the method, replace arg with &(arg).
void add (int *const in)
{
++(*in) ; // increment
(*in) = 5 ; // assign
int x = *in ; // Copy value
}
does the above syntax mean something else in C (i.e pass by value or something), or it's meaningless?
It is meaningless. The program is syntactically ill-formed .

Why can't I assign values to global variables outside a function in C?

Suppose I have a global variable, and I want to assign another variable to it. I've found out that you can assign another value to a global variable inside a function:
int i = 8;
int main(void)
{
i = 9; /* Modifies i */
return 0;
}
However, assignment of the global variable outside of a function does not work!
int i = 8;
i = 9; /* Compiler error */
int main(void)
{
return 0;
}
I get the following error message:
warning: data definition has no type or storage class
warning: type defaults to 'int' in declaration of 'i'
error: redefinition of 'i'
note: previous definition of 'i' was here
int i = 8;
^
Why is this happening?
This is a definition of a global variable, with the optional initialisation to a specific value:
int i = 8;
Note that it is not code which gets ever executed, the variable will just be set up to initially contain the 8. Either consider it "magic" (a helpful model for many things not really defined by the standard) or think of tables with values being copied to memory locations before any code is executed.
This is a piece of code which has no "frame" in which it is executed.
(Or you intend it to be. The compiler is of other opinion, see below.)
i = 9;
There is no function containing it. It is not clear when it should be executed. That is what the compiler does not like.
In C, all code has to be inside a function and will only be executed if that function is called, e.g. from main().
Other language, mostly those which execute "scripts" by interpreting them (instead of code being turned into executeables, e.g. by a compiler) allow to have code anywhere. C is different.
The compiler sees this differently:
i = 9;
it is not inside a function, so it cannot be code
it looks like a variable definition, assuming that you mean it to be an int, i.e. the default
but relying on defaults is not a good idea, so warn about missing type and that the default is used
also, if it is a definition, then it is the second one for i, now that is really wrong, so show an error and fail the compiling
just to be helpful, mention where the first definition of i is
That is how to read the compiler output you have quoted.

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?

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