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.
Related
I have a function whose argument is const char *array[]
array[0] is the path, the rest are the arguments and it ends with NULL.
However, if I try to do execv(array[0], array) I get expected char * const* but argument is of type const char *
How do I go about this, and what is the difference between char * const* and const char *?
void start(const char *array[]) {
execv(array[0], array);
}
First, the error message is not copied correctly. If I run your code in GCC it shows this message instead (note the final *):
note: expected ‘char * const*’ but argument is of type ‘const char **’
which makes more sense as the message you show in the question, does not match the code you show. There is a mismatch in level or indirection.
That said, let's look at this part:
and what is the difference between char * const* and const char *?
Actually it is
and what is the difference between char * const* and const char **?
The first is a pointer to a const pointer to a char. The char that is pointed to is not const and might in theory be changed by execv.
The latter is a pointer to a pointer to a const char. This means, the char that is pointed to mustn't be modified. It might be some read-only string literal in ROM. If you pass such a pointer to a function that will try to modify it, it will fail in one way or the other. Therefore you are not allowed to pass a "pointer to const" to a function that does not expect it to be const.
That is what the compiler is telling you.
Now, how can you get rid of that warning...
To silence your compiler you could try to use some cast and cheat about real nature of that parameter.
In the end the problem will stay the same. A function trying to modify your read-only memory will not be working properly.
Instead you need to make a copy of your data:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void start(const char *array[]) {
int i = 0;
// determine number of strings (including NULL)
while (array[i++] != NULL) ;
// Create an array able to hold pointers to copys
char *my_array[i];
// Copy strings into non-const memory
i = 0;
do
my_array[i] = array[i] ? strdup(array[i]) : NULL;
while (array[i++] != NULL);
execv(my_array[0], my_array);
// Free the memory for the copied strings
i = 0;
do
free(my_array[i]);
while (array[i++] != NULL);
}
int main(void)
{
const char *argv[] = {"ls", "ls", NULL};
start(argv);
return 0;
}
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
In the following code, once I remove the commented part which compares strings, I am getting a seg 11 fault. I am unable to understand why! Rest of the code is working fine. Any help is appreciated!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int compare_scores_desc(const void* scorea, const void* scoreb){
int a = *(int*)scorea;
int b = *(int*)scoreb;
return a-b;
}
int compare_names(const void* namea, const void* nameb){
char** a = *(char**)namea;
char** b = *(char**)nameb;
return strcmp(*a,*b);
}
int main(int argc, char* argv[]){
int scores[7] = {456,234,65,563,67,19,100};
int i;
qsort(scores,7,sizeof(int),compare_scores_desc);
puts("\nThese are the scores in order : \n");
for(i=0;i<7;i++)
printf("%i\n",scores[i]);
char *names[] = {"Krishna","Rama","Bhishma","Arjuna"};
/*qsort(names,4,sizeof(char*),compare_names);*/
puts("------------------");
puts("The names in order are : \n");
for(i=0;i<4;i++)
printf("%s\n",names[i]);
return 0;
}
In compare_names(), you are inappropriately dereferencing the arguments after the cast. The types for the local variables are type char **, but you are casting the arguments as char ** and dereferencing that results in a char *.
namea and nameb are pointers to the elements of your array names[] declared in main(). That means, their types are actually pointer to char *. When you dereferenced these arguments but assigned them to a char **, you cause the local variable to treat the char * as a char ** (your compiler should have issued a diagnostic warning you about this problem). Now, you take a pointer value that is a char *, and dereference it when you pass it to strcmp(). This causes the program to treat sizeof(char *) bytes of the string as a pointer value for the strcmp() function. Since 4 or 8 (or whatever sizeof(char *) is) bytes consisting of printable characters reinterpreted as a pointer value rarely yields a valid pointer, when strcmp() tries to use those pointers, a segmentation fault occurs.
One possible fix is to not dereference when you initialize your local variables. However, the arguments are const void *, so you can avoid the cast altogether if you declare your local variables to be a pointer to a const type:
int compare_names(const void* namea, const void* nameb){
char* const * a = namea;
char* const * b = nameb;
return strcmp(*a,*b);
}
Note that your implementation of compare_scores_desc() fails if a - b results in signed integer overflow. For example, if a is INT_MAX and b is -1. You should fix your implementation to work for all cases.
int compare_scores_desc(const void* scorea, const void* scoreb){
const int *a = scorea;
const int *b = scoreb;
return (*a > *b) - (*a < *b);
}
The problem is in your string comparison function, and here is probably the minimal way to fix it:
int compare_names(const void* namea, const void* nameb){
char* a = *(char**)namea;
char* b = *(char**)nameb;
return strcmp(a,b);
}
The namea and nameb arguments are pointers into the string vector. You understand this, which is why you used the char ** type.
However, all you have to do in the function is retrieve the char * pointers from that array. These char * pointers are already strings. You do not have to dereference them again; just pass them to strcmp.
Your original code has a constraint violation which requires a diagnostic. That should have tipped you off:
/* originally */
char** a = *(char**)namea; /* error: initialization from incompatible type */
You're dereferencing a char **, which produces char *, but you're storing that in a char ** again and dereferencing again, thereby wrongly treating the character data as a pointer.
From the man page of qsort, in an example of sorting strings:
static int
cmpstringp(const void *p1, const void *p2)
{
/* The actual arguments to this function are "pointers to
pointers to char", but strcmp(3) arguments are "pointers
to char", hence the following cast plus dereference */
return strcmp(* (char * const *) p1, * (char * const *) p2);
}
Why is it necessary to have char * const * in the arguments to strcmp()? Isn't char * enough?
strcmp is declared as
int strcmp(
const char *string1,
const char *string2
);
This properly expresses the function's interface contract - which is that strcmp will not modify its input data - and allows the compiler to optimize inside the function (assuming it were not part of the CRT, and likely in assembler already).
const void* p1 says that whatever p1 points at is not changed by this function. If you did
char** p1_copy = (char**) p1;
that would be a setup to potentially break that promise, because you could then do
*p1_copy = "Something else";
So a cast from const void* to char** is said to "cast away const". Legal, but some compilers will warn if you use a cast to both cast away const and otherwise change the type at once.
The cast that doesn't break the promise of the const void* p1 declaration is the one used:
char* const* p1_arg = (char* const*) p1;
Now *p1_arg, the thing p1 points to, can't be changed just like we said. You could change the characters in it though:
*p1_arg[0] = 'x';
The function declaration never said anything about them, and you say you know them to originally be non-const chars. So it's allowable, even though the function doesn't actually do any such thing.
Then you dereference that (as an rvalue) to get a char*. That can legally be passed as the const char* argument to strcmp by automatic const promotion.
Technically, if you wanted to get rid of the consts, the cast would be to char **, not char *. The const is left in the cast because the arguments to cmpstringp are also const.
A comparison function passed to qsort has no business modifying the items it's comparing.
This is why the general case of qsort looks like:
void qsort(void *base, size_t nmemb, size_t size, int(*compar)(const void *, const void *));
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.