ANCI C (C90): Can const be changed? - c

I am confused as to what ANSI specification says about changing a variable declared const can be legally modified through its address. Unfortunately I do not have access to C90 specification but got conflicting pointers:
The keyword const doesn't turn a variable into a constant! A symbol with the const
qualifier merely means that the symbol cannot be used for assignment. This makes the value
re ad -onl y through that symbol; it does not prevent the value from being modified through
some other means internal (or even external) to the program. It is pretty much useful only
for qualifying a pointer parameter, to indicate that this function will not change the data that argument points to, but other functions may. (Expert C Programming: Deep C Secrets: Peter van der Linden)
If an attempt is made to modify an object defined with a const-qualified type through use of an lvalue with non-const-qualified type, the behavior is undefined. If an attempt is made to refer to an object defined with a volatile-qualified type through use of an lvalue with non-volatile-qualified type, the behavior is undefined. (http://flash-gordon.me.uk/ansi.c.txt)
I have seen the latter in C99 specification (n1256.pdf).
Can anyone clarify as to which of the above two views is true please?
Edit: The Expect C Programming actually gives an example to demonstrate the ability to change a const variable using pointer.

It's similar in C90(C89) as C99.
C89 §3.5.3 Type qualifiers
If an attempt is made to modify an object defined with a const-qualified type through use of an lvalue with non-const-qualified type, the behavior is undefined. If an attempt is made to refer to an object defined with a volatile-qualified type through use of an lvalue with non-volatile-qualified type, the behavior is undefined.
Undefined behavior doesn't mean that C prohibits it at all, just the behavior is, well, not defined. So actually the two of your statements are both true.

Don't know about C90, but C11 contains this clause, which I imagine has been there since day one (C11, 6.7.3/6):
If an attempt is made to modify an object defined with a const-qualified type through use
of an lvalue with non-const-qualified type, the behavior is undefined.
The same is true for volatile-qualified objects.

Change in const variable is possible using pointer because it is just a memory location so it will surely accepts the changes made by pointer method.
But since this variable was defined as const so changes in its value will invoke undefined behaviour.

What a declaration such as:
T const *p; //p has type “pointer to const T
means?
A program can use the expression p to alter the value of the pointer object that p designates, but it can’t use the expression *p to alter the value of any objects that *p might designate. If the program has another expression e of unqualified type that designates an object that *p also designates, the program can still use e to change that object.
Thus, a program might be able to change an object right out from under a const-qualified expression.

It is logical that behavior is undefined if you try to modify const variable. Consider embedded platforms where code + constants are placed in ROM. In that case, it is simply not possible to change the value, as it is burnt in forever. Where as if everything resides in RAM, it will likely be changable. That is why the standard says "undefined behavior" in this case - the behaviour must be dependent on platform and compiler.
Statement 1 and 2 are not really mutually exclusive.

What gave you the impression that these two statements were mutually exclusive?
const is just a type qualifier, it's nothing magical.
Memory can still be altered through external means, or through circumventing the compiler's ability to recognize type restrictions. The second statement merely says that attempting to do this will have undefined results; it may or may not change the value.
The way I see it, there is nothing contradictory about either statement.

Related

Is there a standard way to prohibit reading (via assignment) of a variable?

It is known and useful features that:
const type qualifier prohibits writing (modification) of a variable
register storage-class specifier prohibits taking address of a variable
However, is there a standard way to prohibit reading (via assignment) of a variable?
For example, for such variable only writing and/or taking address is permitted.
Reason of the question: need to conditionally prohibit reading (via assignment) of some variable. Does the language provide any way to do it? If no, then why?
UPD. Readers may be interested as well: C: check at compile time or at run time that variable has static storage-class specifier.
No, not as far as I know.
I don't know why, one reason might be that it's something that is only fantastically rarely useful. You (in my opinion) don't actually explain why you want this, which further emphasizes that's a rather convoluted/weird thing to want.
It is known and useful features that:
const type qualifier prohibits writing (modification) of a variable
Yes.
register storage-class specifier prohibits taking address of a variable
True, but that's not the primary purpose of the register storage class.
However, is there a standard way to prohibit reading (via assignment) of a variable?
I take it that you want to forbid ...
_Write_only int x = 1;
int y = x;
... but no, there is no way to do that. If you have an lvalue expression designating an existing object, such as is required to set the value of that object, then that expression can be used to read the current value of the object, too.
Reason of the question: need to conditionally prohibit reading (via
assignment) of some variable.
That's not much of a reason. You seem to just be saying, "I need it because I need it."
Does the language provide any way to do
it? If no, then why?
If by "why" you are asking for us to support our answers, then I refer you to paragraph 6.3.2.1/2 of the standard, where you will find this:
Except when it is the operand of the sizeof operator, the _Alignof operator, the unary & operator, the ++ operator, the -- operator, or the left operand of the . operator or an assignment operator, an lvalue that does not have array type is converted to the value stored in the designated object (and is no longer an lvalue)
That does not leave any room for the kind of semantics you asked about.
If you mean something else by the "why" question then we can only speculate, but I don't personally find the situation in any way surprising. In particular, I don't find the existence of const and register as suggesting that there should be a qualifier with the effect you describe.
If you want an object that can only be written, not read, by a piece of code X then you can declare it outside the scope of X and with linkage that does not afford access from X, and provide a function that X can call to modify the object's value. For example, the object might be declared static at file scope in a different translation unit, with an external writer function declared in that unit:
static int protected;
void set_protected(int value) {
protected = value;
}
Code in other translation units could then use the function to set the variable's value, but could not access the object directly to read its value.
Note well that that all goes out the window if you allow X to get the address of the protected object, as that can be dereferenced to gain access.
You need to hide your variables from the program code
#define CREATE_PROTECTED(var, type, val) type set_##var(type v){static type var = val; return var;}
#define GET(var) set_##var(val)
CREATE_PROTECTED(myvar, size_t, 500);
size_t foo(void)
{
return GET(myvar)
}

difference between static const int vs const int

const int a = 100;
int *p = &a;
*p = 99;
printf("value %d", a);
Above code compiles and I am able to update its value via pointer but code below didn't output anything.
static const int a = 100;
int *p = &a;
*p = 99;
printf("value %d", a);
Can anyone explain this thing.
Both code snippets are invalid C. During initialization/assignment, there's a requirement that "the type pointed to by the left has all the qualifiers of the type pointed to
by the right" (c17 6.5.16.1). This means we can't assign a const int* to an int*.
"Above code compiles" Well, it doesn't - it does not compile cleanly. See What must a C compiler do when it finds an error?.
I would recommend you to block invalid C from compiling without errors by using (on gcc, clang, icc) -std=c11 -pedantic-errors.
Since the code is invalid C, it's undefined behavior and why it has a certain behavior is anyone's guess. Speculating about why you get one particular output from one case of undefined behavior to another isn't very meaningful. What is undefined behavior and how does it work? Instead focus on writing valid C code without bugs.
There are several things going on here:
const does not mean "Put this variable in read-only memory or otherwise guarantee that any attempt to modify it will definitively result in an error message."
What const does mean is "I promise not to try to modify this variable." (But you broke that promise in both code fragments.)
Attempting to modify a const-qualified variable (i.e., breaking your promise) yields undefined behavior, which means that anything can happen, meaning that it might do what you want, or it might give you an error, or it might do what you don't want, or it might do something totally different.
Compilers don't always complain about const violations. (Though a good compiler should really have complained about the ones here.)
Some compilers are selective in their complaints. Sometimes you have to ask the compiler to warn about iffy things you've done.
Some programmers are careless about ignoring warnings. Did your compiler give you any warnings when you compiled this?
The compiler should complain in both cases when you store the address of a const int into p, a pointer to modifiable int.
In the first snippet, a is defined as a local variable with automatic storage: although you define it as const, the processor does not prevent storing a value into it via a pointer. The behavior is undefined, but consistent with your expectations (a is assigned the value 99 and this value is printed, but the compiler could have assumed that the value of a cannot be changed, hence could have passed 100 directly to printf without reading the value of a).
In the second snippet, a is a global variable only accessible from within the current scope, but the compiler can place it in a read-only location, causing undefined behavior when you attempt to modify its value via the pointer. The program may terminate before evaluating the printf() statement. This is consistent with your observations.
Briefly, modifying const-qualified static objects causes a trap and modifying a const-qualified automatic object does not because programs are able to place static objects in protected memory but automatic objects must be kept in writeable memory.
In common C implementations, a const-qualified static object is placed in a section of the program data that is marked read-only after it is loaded into memory. Attempting to modify this memory causes the processor to execute a trap, which results in the operating system terminating execution of the program.
In contrast, an object with automatic storage duration (one defined inside a function without static or other storage duration) cannot easily be put in a read-only program section. This is because automatic objects need to be allocated, initialized, and released during program execution, as the functions they are defined in are called and returned. So even though the object may be defined as const for the purposes of the C code, the program needs to be able to modify the memory actually used for it.
To achieve this, common C implementations put automatic objects on the hardware stack, and no attempt is made to mark the memory read-only. Then, if the program mistakenly attempts to modify a const-qualified automatic object, the hardware does not prevent it.
The C standard requires that the compiler issue a diagnostic message for the statement int *p = &a;, since it attempts to initialize a pointer to non-const with the address of a const-qualified type. When you ignore that message and execute the program anyway, the behavior is not defined by the C standard.
Also see this answer for explanation of why the program may behave as though a is not changed even after *p = 99; executes without trapping.
6.7.3 Type qualifiers
...
6 If an attempt is made to modify an object defined with a const-qualified type through use
of an lvalue with non-const-qualified type, the behavior is undefined. If an attempt is
made to refer to an object defined with a volatile-qualified type through use of an lvalue
with non-volatile-qualified type, the behavior is undefined.133)
133) This applies to those objects that behave as if they were defined with qualified types, even if they are
never actually defined as objects in the program (such as an object at a memory-mapped input/output
address).
C 2011 Online Draft
If you declare a as const, you're making a promise to the compiler that the value of a should not change during its lifetime; if you try to assign a new value to a directly the compiler should at least issue a diagnostic. However, by trying to change a through a non-const pointer p you're breaking that promise, but you're doing it in such a way that the compiler can't necessarily detect it.
The resulting behavior is undefined - neither the compiler nor the runtime environment are required to handle the situation in any particular way. The code may work as expected, it may crash outright, it may appear to do nothing, it may corrupt other data. const-ness may be handled in different ways depending on the compiler, the platform, and the code.
The use of static changes how a is stored, and the interaction of static and const is likely what's leading to the different behavior. The static version of a is likely being stored in a different memory segment which may be read-only.

Why is register array indexing undefined?

Looking at C11 6.3.2.1 paragraph 3:
Except when it is the operand of the sizeof operator, the _Alignof operator, or the unary & operator, or is a string literal used to initialize an array, an expression that has type "array of type" is converted to an expression with type "pointer to type" that points to the initial element of the array object and is not an lvalue. If the array object has register storage class, the behavior is undefined.
Undefined behaviour seems like an odd choice for this situation. Undefined behaviour "imposes no requirements" (3.4.3). In other words, according to only the wording of 6.3.2.1, indexing into (or doing a few other things with) an array declared with register is presumably permitted to compile, run and do exactly what the code looks like it does without issuing an error.
register int a[5];
a[0] = 6; // apparently not required to cause an error?
This seems to contradict the spirit of the keyword, which (per 6.5.3.2) prevents an lvalue's address being taken with &. This is not quite the same thing, but it's certainly related, as implicit array->pointer conversion, and & on an lvalue, generate the same kind of result: a pointer to the object's storage.
The footnote to 6.7.1 makes this relationship explicit:
the address of any part of an object declared with storage-class specifier register cannot be computed, either explicitly (by use of the unary & operator as discussed in 6.5.3.2) or implicitly (by converting an array name to a pointer as discussed in 6.3.2.1).
So if it "can't" be done, why is the conversion undefined instead of erroneous, or (for indexing, where there are a few other options) implementation-defined?
It doesn't read like an oversight in 6.3.2.1, since register's meaning is straightforward enough according to the other mentions; I'd assume it to be perfectly well-defined if that sentence didn't say otherwise. What is there to be in doubt about?
Remember that Undefined Behavior allows everything, including "behaving in a way that's expected on the particular platform". I.e. for a platform that has hardware array registers, you'd want it to compile, for a platform which does not you don't want it to compile. Leaving it UB allows both.
IIRC a 6502 had 256 memory-mapped registers at the start of address space.

C -- Accessing a non-const through const declaration

Is accessing a non-const object through a const declaration allowed by the C standard?
E.g. is the following code guaranteed to compile and output 23 and 42 on a standard-conforming platform?
translation unit A:
int a = 23;
void foo(void) { a = 42; }
translation unit B:
#include <stdio.h>
extern volatile const int a;
void foo(void);
int main(void) {
printf("%i\n", a);
foo();
printf("%i\n", a);
return 0;
}
In the ISO/IEC 9899:1999, I just found (6.7.3, paragraph 5):
If an attempt is made to modify an object defined with a const-qualified type through use
of an lvalue with non-const-qualified type, the behavior is undefined.
But in the case above, the object is not defined as const (but just declared).
UPDATE
I finally found it in ISO/IEC 9899:1999.
6.2.7, 2
All declarations that refer to the same object or function shall have compatible type;
otherwise, the behavior is undefined.
6.7.3, 9
For two qualified types to be compatible, both shall have the identically qualified
version of a compatible type; [...]
So, it is undefined behaviour.
TU A contains the (only) definition of a. So a really is a non-const object, and it can be accessed as such from a function in A with no problems.
I'm pretty sure that TU B invokes undefined behavior, since its declaration of a doesn't agree with the definition. Best quote I've found so far to support that this is UB is 6.7.5/2:
Each declarator declares one identifier, and asserts that when an
operand of the same form as the declarator appears in an expression,
it designates a function or object with the scope, storage duration,
and type indicated by the declaration specifiers.
[Edit: the questioner has since found the proper reference in the standard, see the question.]
Here, the declaration in B asserts that a has type volatile const int. In fact the object does not have (qualified) type volatile const int, it has (qualified) type int. Violation of semantics is UB.
In practice what will happen is that TU A will be compiled as if a is non-const. TU B will be compiled as if a were a volatile const int, which means it won't cache the value of a at all. Thus, I'd expect it to work provided the linker doesn't notice and object to the mismatched types, because I don't immediately see how TU B could possibly emit code that goes wrong. However, my lack of imagination is not the same as guaranteed behavior.
AFAIK, there's nothing in the standard to say that volatile objects at file scope can't be stored in a completely different memory bank from other objects, that provides different instructions to read them. The implementation would still have to be capable of reading a normal object through, say, a volatile pointer, so suppose for example that the "normal" load instruction works on "special" objects, and it uses that when reading through a pointer to a volatile-qualified type. But if (as an optimization) the implementation emitted the special instruction for special objects, and the special instruction didn't work on normal objects, then boom. And I think that's the programmer's fault, although I confess I only invented this implementation 2 minutes ago so I can't be entirely confident that it conforms.
In the B translation unit, const would only prohibit modifying the a variable within the B translation unit itself.
Modifications of that value from outside (other translation units) will reflect on the value you see in B.
This is more of a linker issue than a language issue. The linker is free to frown upon the differing qualifications of the a symbol (if there is such information in the object files) when merging the compiled translation units.
Note, however, that if it's the other way around (const int a = 23 in A and extern int a in B), you would likely encounter a memory access violation in case of attempting to modify a from B, since a could be placed in a read-only area of the process, usually mapped directly from the .rodata section of the executable.
The declaration that has the initialization is the definition, so your object is indeed not a const qualified object and foo has all the rights to modify it.
In B your are providing access to that object that has the additional const qualification. Since the types (the const qualified version and the non-qualified version) have the same object representation, read access through that identifier is valid.
Your second printf, though, has a problem. Since you didn't qualify your B version of a as volatile you are not guaranteed to see the modification of a. The compiler is allowed to optimize and to reuse the previous value that he might have kept in a register.
Declaring it as const means that the instance is defined as const. You cannot access it from a not-const. Most compilers will not allow it, and the standard says it's not allowed either.
FWIW: In H&S5 is written (Section 4.4.3 Type Qualifiers, page 89):
"When used in a context that requires a value rather than a designator, the qualifiers are eliminated from the type." So the const only has an effect when someone tries to write something into the variable.
In this case, the printf's use a as an rvalue, and the added volatile (unnecessary IMHO) makes the program read the variable anew, so I would say, the program is required to produce the output the OP saw initially, on all platforms/compilers.
I'll look at the Standard, and add it if/when I find anything new.
EDIT: I couldn't find any definite solution to this question in the Standard (I used the latest draft for C1X), since all references to linker behavior concentrate on names being identical. Type qualifiers on external declarations do not seem to be covered.
Maybe we should forward this question to the C Standard Committee.

Any way to change a value in a variable which is defined with const qualifier in c?

One thing I got to know now.The question is about the const qualifier in C. In my program I want to change the value of a variable which is defined with a const qualifier.
For example:
int main()
{
const int i=40;
i=50 ; // error
}
But is there any way to change the value of i in the above example.Please let me know it.
Thank you
In any program that has well-defined behavior, i cannot be modified.
C99 §6.7.3 ("Type Qualifiers")/5 states plainly:
If an attempt is made to modify an object defined with a const-qualified type through use
of an lvalue with non-const-qualified type, the behavior is undefined.
i is an object defined with a const-qualified type (const int). It is a non-modifiable lvalue: you cannot modify it because it is const-qualified. The only way to modify it would be to use a cast to remove the const-qualification, which would cause the program to exhibit undefined behavior.
Firstly, you probably shouldn't be doing this.
Secondly, yes you can if you had to by taking its address and referencing it:
const int i = 40;
*(int*)&i = 50;
// i is now 50
Thirdly, please, please do not do this.
Edit: this, as others pointed out, exhibits undefined behavior. You're assuming that your compiler will store this value in an addressable location, which it doesn't have to. (A constant value can be replaced by its immediate value and make "taking address of it" not mean anything.) You were warned.
There are two ways.
First is a way that you aren't interested in. The standard allows this to happen but you probably can't control it so you aren't really interested. The standard says you can define something like
const volatile int *pSomething;
because the implementation might change the thing that is pointed to (for example it might be a timer) but you can prohibit your program from changing it.
Second is a way that you are interested in, but the standard doesn't allow it. yan already gave an answer like this.
*(int*)&i
is a modifiable lvalue according to the rules of what is a modifiable lvalue. However, if you actually try to modify this lvalue, you violate the standard because the actual object i is defined as const. The result is undefined behaviour. The implementation might do what you want, or it might leave i unchanged, it might abort your program, or it might format your hard drive.
Here is a way to do what you might want to do.
int theRealI;
const int *pTheRealI = &theRealI;
#define i (*pTheRealI)
Then i will be read-only, but if you want to modify theRealI you can.

Resources