Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are we allowed to change values of "const" qualified variables?Why pointers are allowed for this,but not assignment?

Consider the following 2 programs prog1 and prog2.Here if I try to change the value of the const qualified variable i using a pointer ptr,I get the warning( not error) "initialization discards qualifiers from pointer target type|",but the program runs nevertheless and displays the new value.But if I try to change the value of i in the second program using an assignment statement,I get the error(not warning) assignment of read-only variable 'i'|.

Here are confusions arising from this premise:

1)Why are we allowed to change the value of a read-only const qualified variable in any circumstance?Doesn't it defeat the purpose of using a const qualifier?Shouldn't we get an error if we attempt to do so?

2)Even if due to some strange reason we are allowed to change values of constants,why the discrimination between changing the value of a read-only const qualified variable using a pointer (which is allowed,with a warning) and through using an assignment operation (which is simply not allowed and gives us an error)?

//prog1
#include <stdio.h>

int main ()
{
 const int i=8;
 int *ptr=&i;
 *ptr=9;
 printf("%d",*ptr);  //Prints new value nevertheless
}

warning: initialization discards qualifiers from pointer target type|

//prog2
#include <stdio.h>

int main()
{
const int i=8;
i=10;
printf("%d",i);
}

error: assignment of read-only variable 'i'|

EDIT for H2CO3

Here I change the value of the const qualified variable more than once.I only get a warning,the same as in prog1

//prog3
#include <stdio.h>

int main ()
{
const int i=8;
int *ptr=&i;
*ptr=9;
*ptr=10;
printf("%d",*ptr);  //Prints 10
}
like image 339
Rüppell's Vulture Avatar asked May 14 '13 12:05

Rüppell's Vulture


1 Answers

1) Why are we allowed to change the value of a read-only const qualified variable in any circumstance? Doesn't it defeat the purpose of using a const qualifier?

Trying to change a const-qualified object through an assignment operator is a constraint violation:

6.5.16 under Constraints:

2 An assignment operator shall have a modifiable lvalue as its left operand.

Modifiable lvalues are defined in 6.3.2.1 (1):

A modifiable lvalue is an lvalue that does not have array type, does not have an incomplete type, does not have a const-qualified type, and if it is a structure or union, does not have any member (including, recursively, any member or element of all contained aggregates or unions) with a const-qualified type.

As a constraint violation, it requires a diagnostic message from the compiler per 5.1.1.3 (1):

A conforming implementation shall produce at least one diagnostic message (identified in an implementation-defined manner) 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. Diagnostic messages need not be produced in other circumstances.

But an implementation is not required to reject invalid programmes, so the diagnostic message could also be a warning instead of an error.

However, modifying an object that is declared const through an lvalue that has not const-qualified type is not a constraint violation, although it invokes undefined behaviour, 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.

Since it's not a constraint violation nor invalid syntax, it doesn't even require a diagnostic message to be emitted.

Shouldn't we get an error if we attempt to do so?

You must get a diagnostic message if you try to modify an object through an lvalue with const-qualified type.

Since that an egregious violation of the declared intentions, most compilers emit an error in these circumstances.

If you try to modify an object with const-qualified type through an lvalue with non-const-qualifed type as in

const int i=8;
int *ptr=&i;
*ptr=9;

the attempt to modify i through the expression *ptr = 9 invokes undefined behaviour, but is not a constraint violation (or syntax error), hence does not require a diagnostic message (and none is given).

There is a diagnostic message emitted for the initialisation

int *ptr = &i;

because that is again a constraint violation, per 6.5.16.1 (1):

One of the following shall hold:

  • the left operand has atomic, qualified, or unqualified arithmetic type, and the right has arithmetic type;
  • the left operand has an atomic, qualified, or unqualified version of a structure or union type compatible with the type of the right;
  • the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) both operands are pointers to qualified or unqualified versions of compatible types, and the type pointed to by the left has all the qualifiers of the type pointed to by the right;
  • the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) one operand is a pointer to an object type, and the other is a pointer to a qualified or unqualified version of void, and the type pointed to by the left has all the qualifiers of the type pointed to by the right;
  • the left operand is an atomic, qualified, or unqualified pointer, and the right is a null pointer constant; or
  • the left operand has type atomic, qualified, or unqualified _Bool, and the right is a pointer.

That diagnostic is however usually a warning and not an error because one may explicitly cast the const away,

int *ptr = (int*)&i;

whereas one cannot cast away the const from i.

Modifying an object through a pointer to a non-const-qualified object type which has been obtained by casting away the const from a pointer to a const-qualified object type is valid if the object pointed to is modifiable. Stupid example:

int i = 8;
const int *cptr = &i;  // valid, no problem adding const
int *mptr = (int*)cptr;
*mptr = 9;             // no problem, pointee is non-const

2) Even if due to some strange reason we are allowed to change values of constants, why the discrimination between changing the value of a read-only const qualified variable using a pointer (which is allowed,with a warning) and through using an assignment operation (which is simply not allowed and gives us an error)?

Directly assigning to an object with const-qualified type is not only a constraint violation, but also an obvious violation of the stated semantics. Declaring an object const explicitly says "I don't want that object to be modified".

Modifying an object through a pointer to a non-const-qualified type is not a constraint violation, and only undefined behaviour if the pointee has const-qualified type. Converting a pointer to a const-qualified type to a pointer to the corresponding non-const-qualified type is allowed, and modifying the pointee through that pointer may be valid, therefore you get only a warning, and only if the conversion is not made explicit.

In the given short example, the compiler could detect that the pointee has const-qualified type and therefore the modification invokes undefined behaviour, but in general such would be difficult, and often impossible to detect. Therefore, the compiler doesn't even attempt to detect the simple cases, it's not worth the effort.

like image 187
Daniel Fischer Avatar answered Oct 12 '22 22:10

Daniel Fischer