0

This is a question from a test.

int main() {
  const int i=1;
  const int *p=&i;
  int j=2;
  const int *q=&j;
  j=3;
  printf("%d", *p+*q);
  int k = 0;
  return 0;
}

I understand that both p and q are pointers to a const int.

I thought that it would give a compile error when I declare the pointer to a non-const value, but it works fine; why is that?

klutt
  • 30,332
  • 17
  • 55
  • 95
  • 4
    Does this answer your question? [What is the difference between const int\*, const int \* const, and int const \*?](https://stackoverflow.com/questions/1143262/what-is-the-difference-between-const-int-const-int-const-and-int-const) – iBug Mar 01 '21 at 19:11
  • Why would you expect a warning? The reassignment is to `j` which is a simple `int`. You can do that all day long. – David C. Rankin Mar 01 '21 at 19:14
  • @Valentin Bichok The compiler can issue an error if you set warnings to be errors. In the program the variable k is declared but not used. And the compiler can inform you about this issuing an error message.:) – Vlad from Moscow Mar 01 '21 at 19:26

3 Answers3

5

I thought that it would give a compile error when I declare the q pointer to a non-const value, but it works fine; why is that??

It would not really make sense to make this non-valid code. If you point with a regular pointer, you can read and write. With a const you can only read, but it's the same underlying type so there's not really anything that can go wrong if you're reading a variable via a regular pointer or a pointer to const. It would be like not being able to read a file from a file system that you have mounted read-only, just because the file itself is not write protected.

Using pointer to const to point at a non-const variable is effectively creating a read-only interface, which makes sense to do. It's commonly used for functions that takes pointers as arguments, but you want to show that the function will not change the content. For instance

char *strcpy(char *dest, const char *src);

This library function reads from src and writes to dest.

However, the opposite will give a warning:

const int i=2;
int *p = &i;

This yields this:

warning: initialization discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]

which makes perfect sense, since otherwise you could very easily be able to modify a constant.

klutt
  • 30,332
  • 17
  • 55
  • 95
2

const int *q=&j; is OK because it adds const. Your pointer q is like a const view to the non-const j. That pointer can be passed around giving others const access to i but no write access.

Of course the other way round would not be possible. If you have a const int k=...; you cannot take away the const by writing int* pp=&k;.

Werner Henze
  • 16,404
  • 12
  • 44
  • 69
1

First, some standard language:

6.3.2.1 Lvalues, arrays, and function designators
...
2 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); this is called lvalue conversion. If the lvalue has qualified type, the value has the unqualified version of the type of the lvalue; additionally, if the lvalue has atomic type, the value has the non-atomic version of the type of the lvalue; otherwise, the value has the type of the lvalue. If the lvalue has an incomplete type and does not have array type, the behavior is undefined. If the lvalue designates an object of automatic storage duration that could have been declared with the register storage class (never had its address taken), and that object is uninitialized (not declared with an initializer and no assignment to it has been performed prior to use), the behavior is undefined.
...

6.3.2.3 Pointers
...
2    For any qualifier q, a pointer to a non-q-qualified type may be converted to a pointer to the q-qualified version of the type; the values stored in the original and converted pointers shall compare equal.
...

6.5.16.1 Simple assignment

Constraints

1    One of the following shall hold:112)
...
      — 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;
...
112) The asymmetric appearance of these constraints with respect to type qualifiers is due to the conversion (specified in 6.3.2.1) that changes lvalues to ‘‘the value of the expression’’ and thus removes any type qualifiers that were applied to the type category of the expression (for example, it removes const but not volatile from the type int volatile * const).
C 2011 Online Draft

So you can take the address of a non-const object and assign it to a const pointer. You can write a new value directly to j, but you can't write a new value to *p, even though j and *p designate the same object.

This is a useful and desirable feature. A common use case is to declare pointer arguments in functions to be const as a promise that the function will not attempt to update the pointed-to object, such as in the strcat function:

char *strcat( char * restrict s1, const char * restrict s2 );

If I write something like

char foo[10] = "foo";
char bar[] = "bar";

strcat( foo, bar );

Even though bar is not const, strcat cannot write to it through the *s2 argument.

But the constraint is asymmetrical - you cannot assign the address of a const object to a non-const pointer.

In general, you can assign from a less restrictive type to a more restrictive type, but not the other way around.

John Bode
  • 119,563
  • 19
  • 122
  • 198
  • I tried checking if what i understood was correct and i'm still not 100% sure. **But the constraint is asymmetrical - you cannot assign the address of a const object to a non-const pointer.** I expected this code to fail for two reasons: 1.I assigned the address of a const object (var) to a non const pointer (ip). 2.I managed to change the value of a const object (var). what am i missing ? `int main () { const int var = 20; int *ip = &var; *ip = 21; printf("Value of *ip variable: %d\n", *ip ); return 0; }` – Valentin Bichok Mar 02 '21 at 07:25