20

The use of const with a pointer can make the pointee not modifiable by dereferencing it using the pointer in question. But why neither can I modify what the pointer is not directly pointing to?

For example:

int a = 3;
const int* ptr = &a;
*ptr = 5;

will not compile. But why does

*(ptr + 2) = 5;

also not compile? I'm not changing what the pointer is pointing to.
So do we have to say that using const with a pointer in such a way not only makes not modifiable what the pointer is pointing to (by dereferencing the pointer) but also anything else, to which the adress we get using the pointer?

I know that in the example I'm trying to access not allocated memory, but this is just for the sake of discussion.

Community
  • 1
  • 1
Root149
  • 389
  • 1
  • 3
  • 11
  • So did you expect the compiler to assume that only the first `sizeof(int)` bytes are `const`? It makes more sense that the compiler will assume that any memory address accessed with `ptr` is a read-only address ("physically" read-only, or at least read-only in the "perspective" of the function which uses `ptr`). – barak manos Dec 21 '14 at 11:35

5 Answers5

25

ptr +2 simply has the same type as ptr namely is a pointer to a const object.

Pointer arithmetic supposes that the object that is pointed to is an array of all the same base type. This type includes the const qualification.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
  • So it's just like int + float = float, so const [type]* + some value = const [type]*, right? – Root149 Dec 21 '14 at 11:51
  • @Root149, something similar, yes. Adding any integer type to a pointer type gives back the same pointer type. – Jens Gustedt Dec 21 '14 at 21:43
  • Only with pointers types decay (everything gets flattened to pointers when pointers are involved, including arrays), where normal integer types get promoted up to a type that can represent everything. – Blindy Dec 22 '14 at 06:21
13

The non-modifiability introduced by const depends on where const is written.

If you write

const int * ptr = &a;

(or int const * ptr = &a;), the const refers to the pointee, so using the pointer for writing to the destination is forbidden.

(OTOH, if wou wrote

int * const ptr = &a;

you couldn't modify ptr.)

In your case, everything involving writing to the destination is forbidden.

This includes *ptr and ptr[0] (which are equivalent), but also everything involving a modification of the destination address, such as *(ptr + 2) or ptr[2].

glglgl
  • 89,107
  • 13
  • 149
  • 217
  • 5
    (and note that `const int *` and `int const *` are the same thing - pointer to const, not const pointer.) – Mat Dec 21 '14 at 11:34
  • also note that given "`int * const ptr = &a;`", you can write "`*ptr = 5;`" but not "`++ptr`". Good coverage of the rules is in the answer at http://stackoverflow.com/questions/1143262/what-is-the-difference-between-const-int-const-int-const-int-const - the first line of the answer - "Read it backwards" - sums it up beautifully. – frasnian Dec 21 '14 at 15:34
  • @frasnian This is essentially what I said - `++ptr` is covered by "you cannot modify `ptr`". – glglgl Dec 21 '14 at 20:45
9

Although other answers explain the technicalities of why it doesn't work, I'd like to offer a more general reason: it's the only thing that makes sense.

The problem is that there is no general way for the compiler to decide whether p + something is the same as p or not, because something can be arbitrarily complex. A rule like "Values pointed to by p and p + 0 are unmodifiable, but other values can still be modified" cannot be checked at compile time: imagine if you wrote:

*(p + very complex expression) = ...

should your compiler be able to figure out if very complex expression is zero? What about

int s;
scanf("%d", &s);
*(p + s) = ...

What should the compiler do in that case?

The only reasonable choice here was to make any value accessed through p unmodifiable.

Clément
  • 12,299
  • 15
  • 75
  • 115
  • This behaviour is not caused by any ambiguity in compiler decision making. It is a simple and correct consequence of the rules of the type system. – Brice M. Dempsey Dec 22 '14 at 09:41
  • @JamesT.Huggett: I'm not sure what you mean by "ambiguity in the compiler decision making"; it's true that the behaviour is a consequence of a typing rule, and my answer just attempts to explain why the rules of the type system were designed this way. – Clément Dec 22 '14 at 13:53
4

Since a pointer can also be used as an array (think about argv), the compiler restricts each access in which the pointer is involved. This way the whole array is read-only.

Philipp Murry
  • 1,660
  • 9
  • 13
  • Well, argv is just a pointer, since writing char* argv[] is equivalent to char** argv. we just create the illusion of an array. – Root149 Dec 21 '14 at 11:40
  • @Root149 Array or not makes no difference when it comes to accessing it, as access works via pointers anyway. – glglgl Dec 21 '14 at 11:45
  • 1
    @Root149: Right. And because of that illusion, the compiler doesn't know if your pointer is also such an 'illusion'. It may be used as an array, like `argv`, and therefore every write-access to it must be restricted (no matter if its `ptr[0]` or `ptr[100]`). – Philipp Murry Dec 21 '14 at 11:52
3

Let's think about the type of the expressions.

const int* ptr = &a;

The type of ptr is const int*.

So the type of *ptr is const int. Not modifiable.

The type of (ptr + 2) is still const int* so the type of *(ptr + 2) is const int which is again not modifiable.

Brice M. Dempsey
  • 1,985
  • 20
  • 16