Read from the name outward, right to left
char * const argv[]
argv[] → argv is an array
* const → of const pointers
char → to char
Rules of thumb:
[]and()next to the name bind first (“array of”, “function returning”).- Then read leftward: each
*is “pointer to”. - A
constapplies to whatever is immediately to its left; if nothing is to its left, it applies to the thing on its right.
| Declaration | Can change the chars? | Can repoint? |
|---|---|---|
char *p | yes | yes |
const char *p (same as char const *p) | no | yes |
char *const p | yes | no |
const char *const p | no | no |
The duplicate-const trap
static const char const *v[]; // looks like "const pointers to const chars"
Both consts are left of the *, so both apply to char: you’ve made the
chars const twice and the pointers not at all. GCC says:
error: duplicate 'const' declaration specifier [-Werror=duplicate-decl-specifier]
What was meant: const char *const v[]. The const for the pointer goes
after the *.
Why execv takes char *const argv[]
int execv(const char *path, char *const argv[]);
exec promises not to repoint the slots, but not to leave the chars alone,
even though it never writes to them. The fully-const signature would have
been const char *const argv[], but C won’t implicitly convert char ** to
const char *const *, so every existing caller would have needed a cast.
POSIX kept the old signature and documents that the strings aren’t modified
(man 3p exec, RATIONALE).
The other direction bites if you write const-correct code:
error: passing argument 2 of 'execv' from incompatible pointer type [-Wincompatible-pointer-types]
note: expected 'char * const*' but argument is of type 'const char * const*'
The usual answer is an explicit cast at the call, (char *const *)argv,
which is safe because of the POSIX guarantee. (In GCC 14+ incompatible
pointer types are an error by default, even without -Werror.)
References: man 3 exec, man 3p exec; cdecl.org to
check your reading.