Passing a char *[] array into a function that takes parameter const char ** - c

So I have a library function that takes in a const char ** as one of its parameters to represent an array of char *s.
void libraryFunc(const char ** parameter);
So what I'm doing currently is this (all in C btw):
char *string1 = "myString";
char *string2 = "myString2";
char *stringArray[2] = { string1, string2 };
libraryFunc(&stringArray[0]);
^That causes a compiler error saying "No matching call to libraryFunc". I've also tried the following:
libraryFunc(stringArray);
libraryFunc(&stringArray);
Can't seem to figure it out.

You can either cast it:
libraryFunc( (const char **) stringArray);
or, preferably, just change the declaration of your array:
char *string1 = "myString";
char *string2 = "myString2";
const char *stringArray[2] = { string1, string2 };
libraryFunc(stringArray);
You cannot implicitly convert a char ** to a const char ** because that only works at the first level of indirection (note in the second extract above you're implicitly converting char * to const char * which, since it's at the first level of indirection, is fine). This question from the comp.lang.c FAQ goes into a bit more detail as to why it works this way.

This is because you are trying to pass
char* - a pointer
to
const char** - pointer to pointer to const char
The use of const is a contract and you cannot meet this contract by going through the indirection of two pointers. It is so because otherwise you would be able always to change this const char applying procedure like this (this is taken from C++ Standard, with my comments):
const char c = 'c';
char* pc;
const char** pcc = &pc; // not allowed (thankfully!)
^^^ here the bundit is hidden under const: "I will not modify"
*pcc = &c; // *pcc is "pointer to const" right? so this is allowed...
*pc = 'C'; // would allow to modify a const object, *pc is char right?
For more details you can follow my other answer on similar topic:
https://stackoverflow.com/a/16390371/1141471
Code online:
http://coliru.stacked-crooked.com/a/74392c59cfc3ef70

Related

Why can I have a const char pointer point to a mutable char array?

Why is this fine:
char a[2];
a[0]='a';
const char* b;
b=a;
printf("%s\n",b);
a[0]='b';
printf("%s",b);
why can a pointer to a constant string point to a non constant string? Also how do string constants work when assigning them to variables? Why can you do this:
const char* b="hello";
b="test";
or
char* b="hello";
b="test";
but you can only do the first line if it is an array? constant or not?
Why can I have a const char * point to a mutable char array?
The const here with b puts a restriction on its use. It is not allowing some new usage, but potentially less. So no opening of Pandora's Box here.
char a[2];
const char* b = a;
b has read-access to the array. The array a[] may still change via a.
a[0]='y'; // OK
b[0]='z'; // leads to error like "error: assignment of read-only location "
Also how do string constants work when assigning them to variables? constant or not?
"hello" is a string literal of type char [6]. With const char* b1 = "hello" declaration and initialization, b1 is assigned a pointer of type char *. This could have been const char *, yet for historical reasons1 it is char *. Since it is char*, char* b2 = "hello"; is also OK.
const char* b1 = "hello";
char* b2 = "hello";
Also how do string constants work when assigning them to variables?
b="test";
As part of the assignment, the string literal, a char array, is converted to the address and type of its first element, a char *. That pointer is assigned to b.
1 const was not available in early C, so to not break existing code bases, string literals remained without a const.
You can always add const-ness to a type. This is a good thing, because it allows you to write functions that make some guarantees.
//Here we know *str is const, so the function will not change what's being pointed to.
int strlength(const char *str) {
int length = 0;
while (*str++)
++length;
return length;
}
int main(void) {
char a[2] = "a"; //a[0] and a[1] are mutable in main(), but not in strlength().
printf("%d", strlength(a));
}
Note that casting away const-ness leads to undefined behavior:
void capitalize(char *str) {
if (isalpha(*str))
*str = toupper(*str);
}
int main(void) {
const char *b = "hello";
capitalize((char*) b); //undefined behavior. Note: without the cast, this may not compile.
}
As for your second question, your first example is correct because the type of a string literal in C (i.e. any sequence of characters between double quotes) is of type const char*. This is valid:
const char *b = "hello"; //"hello" is of type const char*
b = "text"; //"text" is of type const char*
Because casting away const leads to undefined behavior, this code is invalid:
char *b = "hello"; //undefined behavior; "hello" is const char*
b = "text"; //undefined behavior; "text" is const char*
The case for arrays is a little more involved. When used in expressions, arrays act as pointers, but arrays are a fundamentally different type than pointers:
char a[10];
a = "hello"; //does not compile - "hello" is a const char*; a is a char[10]
However, when used in an initialization statement, the rules state that a const char* can be used to initialize a character array:
char a[10] = "hello"; //initialization - a is a char[10], "hello" is a const char*
//a will contain {'h','e','l','l','o',0,0,0,0,0}
Also, don't forget that you can assign a string literal to an array with strcpy:
char a[10];
strcpy(a, "hello");
assert(strcmp(a, "hello") == 0);
//a will contain {'h','e','l','l','o',0,x,x,x,x}
//here x's mean uninitialized

constness and pointers to pointers

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).
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 *.
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.
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** .
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.

Why does passing char** as const char** generate a warning?

I've been getting this warning:
note: expected ‘const char **’ but argument is of type ‘char **’
For now, I'm passing the arguments by casting them to const char **. Is there any other way I can get rid of it?
Short Answer
Can you safely typecast char ** to const char**? No. (Not safely anyway), and the reason is far more subtle than you may think. Can you get rid of it another way? Sure. Load an array of const char* values from your char* values and pass that instead. (or change the callee prototype, but thats cheating =P).
Consider the following code, which essentially does everything you're wishing except invoke a function. The marked line demonstrates the equivalent point-of-cast
const char *s = "Test";
char *p = NULL;
char **pp = &p; // Put address of our pointer in our pointer-to-pointer.
const char **cpp = pp; // Here: assigning char** to const char**
*cpp = s; // perfectly legal; pp and s both finish "char const"
*p = 0; // ru ro raggy
It takes awhile to really stare at this, and admittedly I didn't see it at first either. #sheu did a solid job of catching it about 24 hours before I really thought about it long enough to realize he was right all along (and I actually upvoted that answer before writing this one). Then I thought he was wrong about the same time he thought his answer wasn't applicable. Turns out we were both wrong on that leap, because he was right the first time, I was wrong the second time, and now... ugh.
On VS2012 and VS2010 both the marked line will flag an error without a cast. clang will compile it with a warning in C, but allow it (which I found surprising). Given, you do have to really step out of your happy place to break it, but it is still none-the-less broken.
The rest of this is a diatribe on identifying pointer types, their constness, and what is equivalent to what.
Long Diatribe on Pointers And Const
The warning is because char ** and const char ** are not equivalent (duh). To be correct, you could fix the prototype (callee), or fix the caller (by loading an array of const char * and passing that). But can you safely typecast the first to the second? Hmmm....
Remember, by the standard const goes to the item immediately to its left. Declaring it on the most-left of a data type is a nicety that the language supports, but often introduces confusion or problems. As a rule-of-thumb, if const appears on the far-left of a decl immediately before the type, it applies to the data type; not the subsequent pointer (if any). When it appears to the right of anything it applies to the immediate-left decl-part, be it a data type part or a pointer part, but no matter what it only applies to a single part.
A plethora of samples follows:
No Indirection:
const char ch; // const character. must be initialized.
char const ch; // same as above
Single-Indirection:
char *p; // p is mutable, *p is mutable
const char *p; // p is mutable, *p is const
char const *p; // same as above.
char *const p; // p is const, *p is mutable, must be initialized.
char const *const p; // p is const, *p is const, must be initialized.
Double Indirection:
char **p; // ptr-to-ptr-to-char
// p, *p, and **p are ALL mutable
const char **p; // ptr-to-ptr-to-const-char
// p and *p are mutable, **p is const
char const **p; // same as above
char *const *p; // ptr-to-const-ptr-to-char
// p is mutable, *p is const, **p is mutable.
char **const p; // const-ptr-to-ptr-to-char
// p is const, *p is mutable, **p is mutable.
// must be initialized.
const char **const p; // const-ptr-to-ptr-to-const-char
// p is const, *p is mutable, **p is const.
// must be initialized.
char const **const p; // same as above
char const *const *p; // ptr-to-const-ptr-to-const-char
// p is mutable, *p is const, **p is const.
const char *const *p; // same as above.
char *const *const p; // const-ptr-to-const-ptr-to-char
// p is const, *p is const, **p is mutable.
// must be initialized.
And of course who can leave home without...
char const *const *const p; // const-ptr-to-const-ptr-to-const-char
// everything is const.
// must be initialized.
const char *const *const p; // same as above
So how does this affect your question? When compiling that code in C, without a cast you'll get a compiler warning (or error if compiling with -Werror). When compiling in C++, you'll just plain error because the parameter signature doesn't match. But why?
Because these have no direct equivalence:
const char **p; // ptr-to-ptr-to-const-char
// p and *p are mutable **p is const
char **p; // ptr-to-ptr-to-char
// p, *p, and **p are all mutable
When compiling with clang, the exact warning in C is given as:
main.c:15:9: Passing char ** to parameter of type const char ** discards qualifiers in nested pointer types.
VS2010 and VS2012 both, on the other hand, toss an error:
error C2440: 'initializing' : cannot convert from 'char **' to 'const char **'
It seems odd, but VS is actually more correct (wonders never cease).
And that makes perfect sense. Nestled down in the type declaration is the fact that the first of these does not allow modification to the final data, the second does. From above we know that char ** and const char ** (aka. char const **), are not the same. At the bottom of one is a pointer to a const char, while the other has a pointer to char.
edit: I even answered the wrong question. My answer is completely irrelevant! Ignore me please.
edit 2: after the gentleman question asker clarifies his question, it turns out that my answer is in fact relevant. C'est la vie.
This is a fun bit of C, which makes sense if you think hard enough about it.
Basically, the conversion:
char** ptr;
const char** const_ptr;
const_ptr = ptr; // <-- BAD!
is not allowed.
Why, you might ask? "I'm making things more const! This is obviously a good thing!"
Well, think about this. If that were allowed, then:
const char c = 'A';
char* ptr;
const char** const_ptr = &ptr; // <-- ILLEGAL, but what if this were legal?
*const_ptr = &c;
*ptr = 'B'; // <- you just assigned to "const char c" above.
BAM you're dead. So... no :-)
The warning tells you that the function you're calling expects the given parameter as const char** but you're passing a char** parameter. To get rid of this warning you could
really pass in a const char**
cast your parameter to a const char** (like you're currently doing)
change the function prototype so that the function expects a char**
I still don't think this is right. In the example:
const char c = 'A';
char* ptr;
const char** const_ptr = &ptr; // <-- ILLEGAL, but what if this were legal?
*const_ptr = &c;
*ptr = 'B'; // <- you just assigned to "const char c" above.
The line which breaks this is:
*const_ptr = &c;
Because we are setting a non const sub-pointer to a const pointer. If this where:
const char *const *const_ptr
Then
*const_ptr = &c
would be correct and it wouldn't let you assign to the const char c.
I don't think the compiler should stop you from making something more const.

What's the difference among (const char *str) , (char const *str) and (char *const str)? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
What is the difference between char * const and const char *?
const char * const versus const char *?
as we define a function in c,we may use (const char *str) , (char const *str) or (char *const str) as a variable.What's the difference among them?
The first two are equivalent, const char *str and char const *str both declare str to be a pointer to a char constant (that means that the char itself shouldn't be modified), the third one, char *const str declares str to be a constant pointer (meaning that once assigned it shouldn't be changed) to a char (which itself can be modified freely).
An interesting article regarding how to read type declarations is here, in case you want to check it.
char const * str and const char * str are the same, as const applies to the term on its left or, if there isn't a type on the left side, to the right one. That's why you get a double const error on const char const *. Both are pointers on a constant char. You can change the value of the pointer, but not the dereferenced value:
const char * my_const_str;
my_const_str = "Hello world!"; // ok
my_const_str[0] = "h"; // error: my_const_str[0] is const!
char * const on the other hand is a constant pointer. You cannot change the pointer, but you can change the value dereferenced by the pointer.
char * const my_const_ptr = malloc(10*sizeof(char));
my_const_str[0] = "h"; // ok
my_const_str = "hello world"; // error, you would change the value of my_const_str!
Read C declarations as:
Start at the variable. Look right, look left, then look right again (like crossing the road in the UK). When you see * you say "pointer to", when you see [] you say "array of", when to see () you say "function", etc. In your example cases there is nothing to the right;
const char *str "str is a pointer to a char which is constant"
char const *str "str is a pointer to a constant char" (same as above)
char *const str "str is a constant pointer to a char"
char ch = 'x';
const char cch = 'y';
const char *str = &cch;
char const *str = &cch;
char * const str = &ch;
const char * str1: declare str as pointer to const char
char const * str2: declare str as pointer to const char
char * const str3: declare str as const pointer to char
So in the first two cases, the pointer is mutable, but the data referenced by the pointer is not.
In the last case, the pointer is not mutable, but the data inside is.
So, let's look at some operations:
str1[0]; // legal;
str1[0] += 3; // illegal;
str1 = NULL; // legal;
str3[0]; // legal;
str3[0] += 3; // legal;
str3 = NULL; // illegal;

Why does this allow promotion from (char *) to (const char *)?

Given that scanf has (const char *) in the documentation from Microsoft and the answer to this question what the heck is going when I do the same for (char **) promotion to (const char **)?
Basically why does this compile?
#include <stdio.h>
int main(int argc, char **argv)
{
char szArray[50];
int i = 0;
strcpy(szArray,"10");
/* the following code is upcasting the (char *) to (const char *) */
sscanf(szArray,"%d",&i);
return 0;
}
And why won't this compile?
#include <stdio.h>
void processargs(const char **p)
{
}
int main(int argc, char **argv)
{
processargs(argv);
return 0;
}
Both seem to be doing the same thing to a pointer!
char** -> const char ** is dangerous, since you might end up accidentally modifying the underlying const object.
The correct way to write what you want is:
void processargs(const char * const *p)
{
}
You're allowed to increase access restriction, you just can't decrease it. Going from a normal pointer to a const pointer is fine, going from a const pointer to a normal pointer is not.
The second example doesn't compile because you're not converting a pointer to a const pointer, you're converting from a pointer to one type (char*) to another (const char*). For example, you can change a char** to a char* const*, but not a const char**.
Check if this clarifies for you:
char * a_mutable = /*...*/;
const char * a_constant = /*...*/;
char **pointer_to_mutable = &a_mutable; /* ok */
const char **pointer_to_constant = &a_constant; /* ok */
pointer_to_constant = pointer_to_mutable; /* oops, are you sure? */
*pointer_to_constant = a_mutable; /* valid, but will screw things around */
The last line is valid, since pointer_to_constant is a mutable pointer to a mutable pointer to a constant character, but it would break things since you are making a_constant point to a_mutable. That is why you are not allowed to make pointer_to_constant receive the contents of pointer_to_mutable.
The first example of yours works, because you're converting rvalues of char* to const char*, which is OK (basically because you cannot assign to rvalues). The second doesn't, because the target of a (non-const) pointer is always a lvalue.
Just try (maybe with the aid of the compiler) which operations you can do with char**, which work with const char**, and think if and what types are interchangeable.

Resources