Can we modify the value of a const variable?
Asked Answered
L

6

7

From this article.

Another use for declaring a variable as register and const is to inhibit any non-local change of that variable, even trough taking its address and then casting the pointer. Even if you think that you yourself would never do this, once you pass a pointer (even with a const attribute) to some other function, you can never be sure that this might be malicious and change the variable under your feet.

I don't understand how we can modify the value of a const variable by a pointer. Isn't it undefined behavior?

const int a = 81;
int *p = (int *)&a;
*p = 42; /* not allowed */
Lapides answered 3/9, 2012 at 9:37 Comment(0)
G
11

The author's point is that declaring a variable with register storage class prevents you from taking its address, so it can not be passed to a function that might change its value by casting away const.

void bad_func(const int *p) {
    int *q = (int *) p;            // casting away const
    *q = 42;                       // potential undefined behaviour
}

void my_func() {
    int i = 4;
    const int j = 5;
    register const int k = 6;
    bad_func(&i);                  // ugly but allowed
    bad_func(&j);                  // oops - undefined behaviour invoked
    bad_func(&k);                  // constraint violation; diagnostic required
}

By changing potential UB into a constraint violation, a diagnostic becomes required and the error is (required to be) diagnosed at compile time:

5.1.1.3 Diagnostics

1 - A conforming implementation shall produce at least one diagnostic message [...] if a preprocessing translation unit or translation unit contains a violation of any syntax rule or constraint, even if the behavior is also explicitly specified as undefined or implementation-defined.

6.5.3.2 Address and indirection operators

Constraints

1 - The operand of the unary & operator shall be [...] an lvalue that designates an object that [...] is not declared with the register storage-class specifier.

Note that array-to-pointer decay on a register array object is undefined behaviour that is not required to be diagnosed (6.3.2.1:3).

Note also that taking the address of a register lvalue is allowed in C++, where register is just an optimiser hint (and a deprecated one at that).

Gerik answered 3/9, 2012 at 10:0 Comment(2)
Needs a cast, int * q = (int*)i; *q = 42;.Ulloa
i upvoted this before you gave the details, i would upvote once again if possible. btw i wanted to use the "// ugly but allowed" case but i wasn't sure :)Millennium
D
4

Can we modify the value of a const variable?

Yes, You can modify a const variable through various means: Pointer hackery, casts etc...
Do Read next Q!!

Is it valid code to modify the value of a const variable?

No! What that gives you is Undefined Behavior.

Technically, your code example has an Undefined Behavior.
The program is not adhering to c standard once you modify the const and hence may give any result.

Note that an Undefined Behavior does not mean that the compiler needs to report the violation as an diagnostic. In this case your code uses pointer hackery to modify a const and the compiler is not needed to provide a diagnostic for it.

The C99 standard 3.4.3 says:

Undefined behavior: behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this International Standard imposes no requirements.

NOTE Possible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).

Drucilladrucy answered 3/9, 2012 at 9:40 Comment(6)
Thanks. So, I don't understand what the author has meant? If the behavior is indeed undefined, why is register necessary?Lapides
@Kirilenko register means you can't take the address, thus it can't be modified by the above means.Ulloa
@Daniel Fischer: But with a const variable you can't modify the variable taking its address either, right? Is it different for the compiler?Lapides
@Kirilenko You can cast away the const and then modify it. If you're malicious, you don't care about the fact that that invokes UB. With register, code taking its address shouldn't even compile.Ulloa
Ok, that's what I was looking for. The difference is the interpretation of the behavior by the compiler. It depends on the compiler: in all cases, the user is wrong. But it's not how works restrict: if the user sends two aliased pointers to the function, he won't get an error from the compiler...Lapides
@Kirilenko UB doesn't require a diagnostic. If you break the promise you made by restrict-qualifying a variable, that's UB. And to detect that, the compiler would need an exhaustive dependency analysis. Taking the address of a variable with register storage class is an easily detectable constraint violation, hence a diagnostic is required. (I think it's been made a constraint violation and not UB because it's easily detectable.)Ulloa
O
2

Your code compiles, but it has undefined behavior.

The author's point is to use const and register so that the code no longer compiles:

const int a = 81; 
int *p = (int *)&a; /* no compile error */
*p = 42; /* UB */ 

register const int b = 81; 
int *q = (int *)&b; /* does not compile */
Optometrist answered 3/9, 2012 at 10:0 Comment(0)
S
1

The code fragment indeed invokes undefined behavior.

I 'm not really sure what the author's point is: in order to not let "foreign code" change the value of the variable you make it const so that... UB is invoked instead? How is that preferable? To be frank, it does not make sense.

Shirlyshiroma answered 3/9, 2012 at 9:44 Comment(1)
@UmNyobe: You don't have to. IMO the quoted text is quite explicit.Shirlyshiroma
M
1

I think the author is also talking about this case, which is a misunderstanding of const:

 int a = 1;
 int* const a_ptr = (int* const)&a; //cast not relevant
 int function(int* const p){
     int* malicious = (int*)p;
     *malicious = 2;
 }

The variable itself is not constant, but the pointer is. The malicious code can convert to a regular pointer and legally modify the variable below.

Millennium answered 3/9, 2012 at 9:56 Comment(0)
A
0

I don't understand how we can modify the value of a const variable by a pointer. Isn't it undefined behavior?


Yes, it is undefined behavior:

Quote from C18, 6.7.3/7:

"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 just because the behavior is undefined, it does not mean you potentially can not do that. As far as I can think of, it is indeed the case, that the compiler will, most of the times your program contains any kind of undefined behavior, not warn you - which is a big problem.

Fortunately in this case, when compiling f.e.:

#include <stdio.h>

int main(){
    const int a = 25;
    int *p = &a;

    *p = 26;

    printf("a = %d",a);
}

the compiler will throw a warning:

initialization discards 'const' qualifier from pointer target type [-Wdiscarded-qualifiers] (gcc)

or

warning: initializing 'int *' with an expression of type 'const int *' discards qualifiers [-Wincompatible-pointer-types-discards-qualifiers] (clang)

but despite that the code contains parts which cause undefined behavior and you can never be sure what it will print on any execution, you get that malicious program compiled (without -Werror option of course).


Can we modify the value of a const variable?

So, yes - unfortunately. One can actually modify a const object, but you never ever should do that, neither intentionally nor by accident.

The method to using register keyword might be efficient because the address of a register marked variable can´t have its address taken - means you cannot assign a pointer with the address of the relative variable nor pass it to a function as argument of the respective pointer type.

Agatha answered 12/3, 2020 at 17:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.