2

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

  1. 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).
  2. 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).

3
  • You're trying to convert T ** to const T **, not T * to const T *. Commented Jan 21, 2013 at 15:34
  • @melpomene: I don't see the difference. Commented Jan 21, 2013 at 15:35
  • No, the first one is a pointer to pointer to T, the second one is a pointer to pointer to const T, the third one is a pointer to T, and the fourth one is a pointer to const T. None of the pointers are const. Commented Jan 21, 2013 at 15:36

4 Answers 4

5

The reason char ** -> const char** is a "dangerous" conversion is the following code:

const char immutable[] = "don't modify this"; void get_immutable_str(const char **p) { *p = immutable; return; } int main() { char *ptr; get_immutable_str(&ptr); // <--- here is the dangerous conversion ptr[0] = 0; } 

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.

I thought a pointer to an address can always be casted to a const pointer

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 if T is char * then const T is char * const, not const char * (at this point it's probably worthwhile not writing the const on the left any more: write char const * and you won't expect it to be the same as T const where T is char *).

You can safely convert char ** to char * const *, and (for reasons that require a little more than just the simple rule) you can safely convert char ** to char const * const *.

Sign up to request clarification or add additional context in comments.

1 Comment

@SteveJessop - could you please elaborate on the last sentence (for reasons that require a little more than just the simple rule) ?
1

The key is that not the pointer is const. To declare a const pointer, use char *const ptr; or to declare a const pointer to a const pointer, char *const *const ptr;. const char **ptr is a pointer to pointer to const char.

8 Comments

@RamyAlZuhouri const char **ptr, sorry for having confused it. The pointer, however, still won't be const.
Wuh, I'm sorry for my bad wording. My intention was to make the memory immutable for the function body, therefor, const char** ptr should be fine, if it makes the char const, right?
@NiklasR Yes, it is. By the way, you can check stuff like this here :-)
Thanks. But then, I still do not understand why I can't pass a char** ptr (which is a mutable pointer to a mutable pointer to a mutable char), to a function expecting a const char** ptr (which is mutable pointer to a mutable pointer to an immutable char). Doesn't the rule applay, that muttable objects can always be treated immutable, but not the other way round?
@NiklasR Because that's not what you're doing. You're trying to convert T ** to const T **, not T * to const T *.
|
0

Actually if there is a function that accepts a const char** and you pass a char** , this can lead to a problematic situation and viceversa.

In your specific case you expect that the memory is immutable, but it's not immutable and may change at any time. In a multithreading environment you would expect this memory to be thread safe, and as long as it's living in the stack or heap, you wouldn't need a mutex to access to it.

All this is oriented to avoiding errors, but if you are sure that this wouldn't lead to an error you can simply cast the pointer to const char** .

7 Comments

Why is the memory not immutable (at compile time of course, I understand that memory can not be made immutable at runtime) when marking it with const? I want to know why the compiler tells me that the types are incompatible, and not just "because they are". @melpomene said that there is a difference in casting T* to const T* and casting T** to const T**, but why? Thanks.
There may be some immutable memory at runtime also, for example const char* str= "hello", that's immutable memory and if you try to change it you get a segmentation fault.
"Actually if there is a function that accepts a const char** and you pass a char** , this can lead to a problematic situation and viceversa." - only the vice versa part is true. Not trying to mutate something mutable is fine, trying to mutate something immutable isn't.
One thing that may create problems is the assumption that this memory is really immutable, and getting bad surprises when it changes.
@H2CO3: This was exactly my assumption, and this is why I do not understand why the implicit casting triggers an error message.. :(
|
0

You cannot pass char ** into const char ** because the compiler cannot guarantee const correctness.

Suppose you had the following code (and it compiled):

void foo(const char **ppc, const char* pc) { *ppc = pc; // Assign const char* to const char* } void bar() { const char c = 'x'; char* pc; foo(&pc, &c); // Illegal; converting const char* to const char**. Will set p == &c *pc = 'X'; // Ooops! That changed c. } 

See here for the same example without the function calls.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.