In C, const char *p
is sometimes called a "read-only" pointer: a pointer to a constant object (in this case, a char
).
It would seem that either
const char **p
const char *const *p
would be the equivalent declarations for a read-only pointer to a pointer, depending on how many levels of indirection are immutable.
However, compilers (gcc, clang) generate a warning.
My Questions: How do you pass a pointer to a pointer (like char **p
) to a function as a "read-only" pointer without generating a warning? If an explicit cast is required, why in the case of char **p
and not char *p
?
Here is a concrete example of what I'm trying to achieve.
This code treats char *ptr
as a read-only pointer.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void readonly(const char *ptr)
{
// ... do something with ptr ...
// but modifying the object it points to is forbidden
// *ptr = 'j'; // error: read-only variable is not assignable
}
int main(void)
{
char *ptr = malloc(12*sizeof(char));
strcpy(ptr, "hello world");
printf("before: %s\n", ptr);
readonly(ptr);
printf("after: %s\n", ptr);
free(ptr);
return 0;
}
The qualifier const
is added in the function call without any complaints.
I would expect that a similar function call should be possible with a pointer to a pointer.
void readonly(const char *const *ptr)
{
// ... do something with ptr ...
// but modifying the object it points to is forbidden
// **ptr = 'j';
}
int main(void)
{
char **ptr;
ptr = (char **) malloc(2*sizeof(char *));
ptr[0] = malloc(14*sizeof(char));
strcpy(ptr[0], "hello world 0");
ptr[1] = malloc(14*sizeof(char));
strcpy(ptr[1], "hello world 1");
printf("before: %s %s\n", ptr[0], ptr[1]);
readonly(ptr);
printf("after: %s %s\n", ptr[0], ptr[1]);
free(ptr[1]);
free(ptr[0]);
free(ptr);
return 0;
}
The clang compiler (version 6.0.0) gives the most human-readable warning.
warning: passing 'char **' to parameter of type
'const char *const *' discards qualifiers in nested pointer types
[-Wincompatible-pointer-types-discards-qualifiers]
readonly(ptr);
^~~
note: passing argument to parameter 'ptr' here
void readonly(const char *const *ptr)
But gcc (8.1.1) also gives a warning.
Aside: It seems strange that clang says that passing char **
discards the qualifier, when I'm trying to add the qualifier?
How do you pass a pointer to a pointer (like char **p
) to a function as a "read-only" pointer without generating a warning?
If an explicit cast is required, why in the case of char **p
and not char *p
?
A pointer is a reference to data, so the only way to do so is to copy the data, there's nothing like a read-only memory permission (unless it's the part holding the executable itself).
A constant pointer is one that cannot change the address it contains. In other words, we can say that once a constant pointer points to a variable, it cannot point to any other variable. Note: However, these pointers can change the value of the variable they point to but cannot change the address they are holding.
A pointer to constant is a pointer through which the value of the variable that the pointer points cannot be changed. The address of these pointers can be changed, but the value of the variable that the pointer points cannot be changed.
In the constant pointers to constants, the data pointed to by the pointer is constant and cannot be changed. The pointer itself is constant and cannot change and point somewhere else.
Your function readonly(const char *ptr)
promises that it will not touch what is behind the pointer.
That's the reason why a caller may trustfully pass a pointer to a const memory area (passing a const char *ptr
).
And of course it is no problem to pass a non-const char *ptr
to this function as well, because your function promises more security than needed. That's why the compiler follows your wish without any warning or note whatsoever.
However this automatism does only work for the 1st indirection level.
The Standard claims in 6.5.16.1 for an assignment, that "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 last part of the sentence means that adding a qualifier to the pointed-to type is no problem.
And the first part claims "compatible" types. And (I think,) 6.7.3 (11) does describe this for qualified types: "For two qualified types to be compatible, both shall have the identically qualified version of a compatible type."
Reading this, your pointed-to types are not considered as compatible (even if it would be possible to assign one to the other).
Hence I would say that the clang warning about discarding qualifiers is a bit misleading, but it refers to the non-identically qualified pointed-to types.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With