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

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.

Related

const qualifier in qsort compare function

I have this to sort a string array (argv itself) in two ways with qsort. i started with the linux man 3 qsort example. There cmpstringp is only one line:
/* 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(*(const char **) p1, *(const char **) p2);
I tried to reformulate this and it compiles without warnings only with
char *const* sp1 = vep1
i.e. const belongs in the middle - it is the argv entry (?). At least that is what qsort declaration demands. The above cast from man page seems different and complicated.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* vep: void pointer to elements being qsorted
sp: string pointer (char **) */
int
cmpstringp(const void *vep1, const void *vep2) {
char *const *sp1 = vep1,
*const *sp2 = vep2;
return strcmp(*sp1, *sp2);
}
/* Make char* from void*, and even char */
int
cmpchar(const void *p1, const void *p2) {
const char *cp1 = p1,
*cp2 = p2;
char c1 = *cp1,
c2 = *cp2;
if (c1 == c2)
return 0;
else
return c1 > c2 ? 1 : -1;
}
/* Sort cmd line args in two ways with qsort */
int main(int argc, char **argv) {
/* sort chars of each string */
for (int i = 1; i < argc; i++)
qsort(argv[i], // base / first
strlen(argv[i]), 1, // n_elems, elem_size
cmpchar);
/* sort argv strings 1 to argc-1 */
qsort(argv + 1,
argc - 1, sizeof *argv,
cmpstringp);
for (int j = 1; j < argc; j++)
puts(argv[j]);
return 0;
}
The second cmp function cmpchar has one extra assignment level. Without there would be just some harmless stars like if (*cp1 == *cp2).
How do I sort the elements of argv in C? has a solution like man page, with a cast directly in strcmp().
But isn't my cast-free approach more correct? I got warnings until I put the const where it belongs.
The const in the middle of char *const *sp1 = vep1 means that whatever is designated by *sp1 is const. This is required because the const void * says that the memory directly pointed to by the pointer is supposed to be const.
You're right in that const void * is not assignable to const char ** without a cast because it would not be const correct. I personally try to avoid casts as much as is meaningfully possible, and therefore would prefer your code over the one that uses casts.
However It would be somewhat more correct to use const char *const *sp1, because there is no reason why cmpstringp shouldn't actually work for const char ** too!
Reordered and aligned it becomes more clear what is converted and what is conserved from right to left:
int cmpstringp(void const *vep1, const void *vep2) {
char * const *sp1 = vep1, ...
The const is copied, and the "to void" is replaced by "to pointer (to char). It does not even have to be a pointer (e.g. a struct). If, then I doubt this const-ness can be spread onto all references, just because a "compare" function is not supposed to modify at all. It may modify all objects, but not the ones being sorted.
This is all very interesting but logically complicated. Like having a row of boxes with letters inside. First you sort the letters inside each box according to themselves (not touching the boxes), then you sort the boxes according to the letters (but not touching them, the letters).
Thank you for comments and answer.
6.5.16.1 Simple assignment probably handles this case.
3 bullets for pointer assignment seem to demand:
compatible types, or one is void
left side has all the qualifiers of the right side.
null pointer on the right is no problem

execv arguments error - "expected char * const* but argument is of type const char *"

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;
}

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

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

QSORT function in C

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.

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.

Resources