Except when it is the operand of the sizeof
or unary &
operators, an expression of type "N-element array of T
" will be converted to an expression of type "pointer to T
", and the value of the expression will be the address of the first element of the array. The result is not an lvalue, meaning it cannot be the target of an assignment.
So, given the declaration
char mystr[20] = "hello there!";
the following are all true:
Expression Type Decays to Result
---------- ---- --------- ------
mystr char [20] char * address of mystr[0]
&mystr char (*)[20] n/a address of mystr
*mystr char n/a value of mystr[0], or 'h'
mystr[i] char n/a i'th character
&mystr[i] char * n/a address of mystr[i]
sizeof mystr size_t n/a 20 (number of bytes in array)
The address of the array is the same as the address of the first element of the array (no storage is set aside for a mystr
variable separate from the array elements themselves), so the expressions mystr
, &mystr
and &mystr[0]
all return the same value, but the types of the expressions are different; mystr
and &mystr[0]
both have type char *
, but &mystr
has type char (*)[20]
, or "pointer to 20-element array of char
".
The results for str2
are the same.
Given the declaration
char *p_mystr;
we get the following
Expression Type Result
---------- ---- ------
p_mystr char * value in p_mystr
*p_mystr char value of character pointed to by p_mystr
&p_mystr char ** address of p_mystr variable
sizeof p_mystr size_t number of bytes in pointer value
The table for p_str2
is the same.
Now it's a matter of matching up types. p_mystr = mystr
works because both expressions have type char *
; p_mystr
winds up pointing to mystr[0]
. Similarly, *p_mystr = mystr[2]
works1 because both expressions have type char
. Same for val = *p_mystr++
, although note in this case that *p_mystr++
is parsed as *(p_mystr++)
; the increment will be applied to the pointer value, not the character value.
Note that an expression like p_mystr = &mystr;
should get you a diagnostic to the effect that the types don't match.
Your question title mentions const
pointers and variables, although that doesn't appear in the question body. Basically, for any type T
:
T *p0; // p0 is a non-const pointer to non-const T
const T *p1; // p1 is a non-const pointer to const T
T const *p2; // p2 is a non-const pointer to const T
T * const p3; // p3 is a const pointer to non-const T
const T * const p4; // p4 is a const pointer to const t
T const * const p5; // p5 is a const pointer to const t
Which breaks down as:
- You can write to
p0
(change it to point to something else) and *p0
(change the thing being pointed to);
- You can write to
p1
and p2
(change them to point to something else), but you cannot write to *p1
or *p2
(cannot change the thing being pointed to);
- You cannot write to
p3
(cannot change it to point to something else), but you can write to *p3
(change the thing being pointed to);
- You cannot write to
p4
or p5
(cannot change them to point to something else), nor can you write to *p4
or *p5
(cannot change the thing being pointed to).
So, if you declared something like
const char *cptr = mystr;
you could not modify the contents of mystr
through the cptr
variable, even though mystr
is not declared const
itself.
Note that trying to go the other way, such as
const int foo = 5;
int *fptr = (int *) &foo;
*fptr = 6;
invokes undefined behavior; it may or may not work, depending on the implementation (the language definition allows implementations to put const
-qualified objects in read-only memory).
1. If p_mystr
is pointing somewhere valid, that is.