I'm very much confused about the const
keyword. I have a function accepting an array of strings as input parameter and a function accepting a variable number of arguments.
void dtree_joinpaths(char* output_buffer, int count, ...);
void dtree_joinpaths_a(char* output_buffer, int count, const char** paths);
dtree_joinpaths
internally invokes dtree_joinpaths_a
after it has built an array of strings from the argument list.
void dtree_joinpaths(char* output_buffer, int count, ...) {
int i;
va_list arg_list;
va_start(arg_list, count);
char** paths = malloc(sizeof(char*) * count);
for (i=0; i < count; i++) {
paths[i] = va_arg(arg_list, char*);
}
va_end(arg_list);
dtree_joinpaths_a(output_buffer, count, paths);
}
But the gcc
compiler gives me the following error message:
src/dtree_path.c: In function 'dtree_joinpaths':
src/dtree_path.c:65: warning: passing argument 3 of 'dtree_joinpaths_a' from incompatible pointer type
When I change char** paths = malloc(count);
to const char** paths = malloc(count);
, this error is not showing up anymore. What I don't understand is, that
- I thought a pointer to an address can always be casted to a const pointer, but not the other way round (which is what is happening here imo).
- This example works: http://codepad.org/mcPCMk3f
What am I doing wrong, or where is my missunderstanding?
Edit
My intent is to make the memory of the input data immutable for the function. (in this case the paths
parameter).
Best Answer
The reason
char **
->const char**
is a "dangerous" conversion is the following code:The above code attempts to modify a non-modifiable object (the global array of
const char
), which is undefined behavior. There is no other candidate in this code for something to define as "bad", so const-safety dictates that the pointer conversion is bad.C does not forbid the conversion, but
gcc
warns you that it's bad. FYI, C++ does forbid the conversion, it has stricter const-safety than C.I would have used a string literal for the example, except that string literals in C are "dangerous" to begin with -- you're not allowed to modify them but they have type array-of-
char
rather than array-of-const char
. This is for historical reasons.A pointer-to-non-const-T can be converted to a pointer-to-const-T.
char **
->const char**
isn't an example of that pattern, because ifT
ischar *
thenconst T
ischar * const
, notconst char *
(at this point it's probably worthwhile not writing theconst
on the left any more: writechar const *
and you won't expect it to be the same asT const
where T ischar *
).You can safely convert
char **
tochar * const *
, and (for reasons that require a little more than just the simple rule) you can safely convertchar **
tochar const * const *
.