During compilation of a call to the following function:
char* process_array_of_strings(const char** strings);
GCC complains when a char** is passed as argument:
note: expected ‘const char **’ but argument is of type ‘char **’
While the function does not alter the characters (hence the const) it does duplicate the array of pointers in order to modify the character pointers themselves, so constant pointers are definitely undesirable here.
Compilation succeeds and the program appears to work. So how is the programmer supposed to handle this warning?
Make the conversion explicit with a cast and the compiler will be happy:
process_array_of_strings((const char**) foo);
In these cases you have to explicitly say that you know what you're doing.
This is why char ** is not automatically converted to const char ** in C++, and why the C compiler issues a warning while allowing it.
/* This function returns a pointer to a string through its output parameter: */
void get_some_string(const char ** p) {
/* I can do this because p is const char **, so the string won't be modified. */
*p = "unchangeable string in program core";
}
void f() {
char * str;
/* First, I'll call this function to obtain a pointer to a string: */
get_some_string(&str);
/* Now, modify the string: */
for (char * p = str; *p; p++)
*p = toupper(*p);
/* We have just overwritten a constant string in program core (or crashed). */
}
From your description of what process_array_of_strings() does, it could just as well take const char * const * because it modifies neither the pointers nor the characters (but duplicates the pointers elsewhere). In that case, the above scenario would not be possible, and compiler theoretically could allow you to convert char ** to const char * const * automatically without warnings, but that's not how the language is defined.
So the answer is obviously that you need a cast (explicit). I've written up this expanation so that you can fully understand why the warning appears, which is important when you decide to silence one.
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;
}
I was studying "C complete reference" by Herbert Schildt and got stuck on the "const" explanation due by the pointer * he used at the same time with the const explanation.
here is the code he used:
#include <stdio.h>
void dash(const char *str);
int main()
{
dash("this is a test");
return 0;
}
void dash(const char *str)
{
while (*str)
{
if (*str == ' ')
{
printf("%c", '-');
}
else
{
printf("%c", *str);
}
str++;
}
}
I've tried to search about the pointer * and got some answers about adresses but why did he use it in this example? His book didn't explain this and i haven't found other examples with this kinda use of pointer *.
Other question is, why is the loop "while (*str)" correct if it has no condition?
const char *str in a parameter declaration indicates that the function will not try to modify the values that the str pointer points to. This means that you can call the function with a constant string. If you don't have const in the declaration, it means that the function might modify the string, so you can only call it with writable strings.
As an example, a function like strcpy() declares has const on the second parameter (the source string), but not on the first parameter (the destination). It can (and usually does) modify the destination, but not the source.
Many people are confused when start learning C
const char *ptr
It is a pointer which is referencing the const char. The pointer can be modified. But is you try to write to the referenced object the compiler will complain: https://godbolt.org/z/d9znF-
Example:
const char c;
const char *ptr = &c;
*ptr = 'p'; // -- illegal - the compiler will complain
ptr++; // -- legal
to declare the constant pointer to the not constant object:
char * const ptr;
now ptr cannot be changed but the referenced object can: https://godbolt.org/z/h7WWex
char c;
char * const ptr = &c;
*ptr = 'p'; // -- legal
ptr++; // -- illegal - the compiler will complain
to declare const pointer to const object
const char * const ptr;
now the pointer and the referenced object cannot be modified: https://godbolt.org/z/x2xBcZ
const char c;
const char * const ptr = &c;
*ptr = 'p'; // -- illegal - the compiler will complain
ptr++; // -- illegal - the compiler will complain
It's a way of promising that the content the pointer is pointing at will not be altered. It's also a way of suppressing warnings without explicit casts.
Consider this:
void dash(char *str) // Removed const
{
// Code
}
int main() {
const char p[] = "this is a test";
dash(p);
}
Now the compiler will emit this:
k.c: In function ‘main’:
k.c:23:10: warning: passing argument 1 of ‘dash’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
23 | dash(p);
| ^
k.c:4:17: note: expected ‘char *’ but argument is of type ‘const char *’
4 | void dash(char *str)
| ~~~~~~^~~
Since you're not writing to it, this warning is nothing to worry about. But it's good practice to avoid warnings. In this case, we have two alternatives. Either the function may modify the string or it may not. If there's no way it will modify it, then there's no reason to explain to the compiler and the reader that this indeed is the case.
Sidenote. String literals, like "this is a test" has undefined behavior if you modify them, so the program might crash (or not). However, their type is is of type (char*) with no const. The reason is backwards compability. In C++, their type is const char*
Note that the const is a promise by convention, not by the compiler. This code will modify the original string and also compile without warnings:
#include <stdio.h>
void foo(const char *str)
{
// Casting comes with great responsibility
// You're just saying to the compiler
// "Trust me and shut up"
char *ptr = (char*) str;
ptr[2]='A';
ptr[3]='T';
}
int main()
{
const char p[] = "this is a test";
foo(p);
puts(p);
}
output:
$ ./a.out
thAT is a test
As I said, the above will compile without warning. If you remove the cast, you'll get this:
k.c:5:17: warning: initialization discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
5 | char *ptr = str;
| ^~~
Do note that since p is declared as const this is undefined behavior. However, you instead write main like this:
int main()
{
char p[] = "this is a test";
foo(p);
puts(p);
}
then, the program is completely valid. And even though you pass a writable string to the function foo, you'd expect it to not change, since foo takes a constant pointer as argument. But as you can see, such things can be bypassed.
Be very careful with void pointers
Note that this is perfectly valid for ANY type T:
T x;
T *p;
p = (void*) &x;
This is because you can safely cast a pointer to void and back. However, this is NOT valid in the general case:
T x;
Q *p;
p = (void*) &x;
However, because of the cast, you will not get a warning. But this code invokes undefined behavior.
Moral lesson
Casting is NOT the goto solution for warnings. Instead, you should REALLY carefully consider if your cast match your intentions. If you're intentions here is to just get rid of the warning, the right solution is to remove the const for the parameter. If you're intentions with adding the cast is "I know that this function promises to not modify the argument, but I have good reasons for both promising that and then instantly break that promise" then a cast is correct.
Real world example
Just to give a real world example of how it can go wrong. I looked in this question where I saw this:
void * func_return();
void (*break_ptr)(void) = (void *)func_return;
I told OP that the cast is wrong. I got the response that without a cast, the compiler complained. Well, it complained because the pointer is WRONG. The function prototype declares a function taking an unspecified number of arguments and returning a void pointer. The function pointer is a pointer to a function taking NO arguments returning nothing. So in this case, the proper pointer declaration and initialization would be this:
void * func_return();
void *(*break_ptr)() = func_return;
But this would probably be better:
void * func_return(void);
void *(*break_ptr)(void) = func_return;
Note that since a pointer of any type can be safely cast to void* and back. But in this case OP was not casting it back, but to another type. If OP had done it correctly, the cast would just be clutter, but in this case it did hide the REAL error.
In c we can manipulate an array like a pointer with the right pointer arithmatic like he used and we can manipulate it like an array!
const char *str
is a pointer to const char OR an array of const char data types!
In a function, all parameters are passed by value (arrays are no exception). When you pass an array in a function it "decays into a pointer". And when you compare an array to something else, again it "decays into a pointer"
so we can write the while loop again in different way:
void dash(const char *str)
{
int i = 0;
while (str[i])
{
if (str[i] == ' ')
{
printf("%c", '-');
}
else
{
printf("%c", str[i]);
}
++i;
}
}
Now, the first syntax (with the pointer deref operator * is more effecient than array syntax).
in general array name or the address of the first array element (of any type), can decays to a pointer of the same data type!
In his implementation, he behaves the str as a const char pointer, in the while loop he is derefrence the pointer (like str[i], with the brackets) and in the last line (str++) he is moving the pointer to points to the next char element (which usualy knwon as pointer arithmetics).
In this case, read the definition from right to left:
const char *str // str is a pointer to a const char
The address of str can change while the char it points to cannot.
To answer you other question, while (*str) will continue to interate until *str == '\0'. '\0' is used to mark the end of a string in C.
What the program does, if you're unsure, is print it, replacing ' ' with '-'. In your example, "this-is-a-test" would be printed. Note: the string "this is a test" is not modified.
The * is related to pointers but it has two uses.
In the declaration, * is used to declare the pointer type, as in:
const char *str;
Where str is a pointer to a const char (or multiple const char stored in sequence, C doesn't care about the difference).
In an expression, * is used to dereference a pointer, get the value it points to. As in:
printf("%c", *str);
Where *str is that const char itself that the pointer str is pointing to.
Related to pointers, there's also & that does the other way around. It gets the pointer of any value you have stored in memory.
The importance of const here is not related to pointers, it's related to the fact you're passing a string literal to dash(). Unlike strings that are stored in the heap or the stack, string literals cannot be modified and should be treated as const for their immutability.
If we have void foo(const char * const ** bar); and we call it with char *** bar = malloc(...); such as foo((const char * const **) bar); It returns:
error: to be safe all intermediate pointers in cast from ‘char ***’ to ‘const char * const**’ must be ‘const’ qualified [-Werror=cast-qual]
Trying to pass it in without casting results in a type warning, but passing a char * into a const char * works, I can also pass a char *** into a char ** const * function parameter, but I want more read-only qualifier than just a single level.
The intent is that the function assumes the entire pointer data and pointer being passed in are read-only, such that the pointer won't be written to in any way.
I understand that the flag is an extra one, but it's defined as:
-Wcast-qual
Warn whenever a pointer is cast so as to remove a type qualifier from the target type. For example, warn if a const char * is cast to an ordinary char *.
As far as I can tell, no type qualifier is being removed, rather we're adding type qualifiers to let the user know that the function doesn't change any data.
Why is the previously mentioned code triggering the extra warning -Wcast-qual? Is there a better way of going about passing a completely malloc'd triple pointer as a read-only function?
Why does passing a char* to a function that expects a char const* work?
It's simple to understand that. You are not able to modify the string in the called function. That's OK.
Why does passing a char** to a function that expects a char const** not work?
This sounds non-intuitive but there is a reason. Take this analogy with an int.
const int a = 10;
void foo(int const ** ap)
{
*ap = &a;
}
void bar()
{
int * p = NULL;
foo(&p);
*p = 20;
}
If you were allowed to use that, you are able to modify a, which is const, in bar indirectly.
That's why &p, whose type is int**, is not allowed to be cast to int const** implicitly.
TL;DR:
const char * const ** -> const char * const * const *
To finish the question, answered in the comments by /users/2410359/chux (asked them to make a post, but they haven't). The solution was to use a const char * const * const * which does not trigger the -Wcast-qual flag. Basically what I had was not true read-only qualifiers, I needed another level.
Here is the compile warning i have:
src/Debugger.c:219:52: warning: passing argument 2 of ‘Debugger_Command[i].Callback’ from incompatible pointer type
Debugger_Command[i].Callback(argc, argv);
^
src/Debugger.c:219:52: note: expected ‘const char **’ but argument is of type ‘char **’
Here is the relevant source code:
/* Definition */
typedef void (*Debugger_Callback_t)(int argc, char const * argv[]);
typedef struct tagDebugger_Command_t {
/* ... */
Debugger_Callback_t Callback; /**< Callback */
} Debugger_Command_t;
Debugger_Command_t const Debugger_Command[] = { /* ... */ }
/* Use of the callback where the warning occurred */
char *argv[DEBUGGER_ARG_COUNT];
Debugger_Command[i].Callback(argc, argv);
What is wrong with passing a non const variable as a const parameter ?
To my understanding, this is to make sure the string wont be modified inside the function. So why is the warning occuring ?
Compiler: gcc version 4.9.2 (GCC) on Windows/Cygwin
Because technically you may be breaking a promise by passing a non-const pointer into a const pointer parameter.
"It is dangerous to take away const-ness"
http://c-faq.com/ansi/constmismatch.html
Here is the content of the above link:
You can use a pointer-to-T (for any type T) where a pointer-to-const-T is expected. However, the rule (an explicit exception) which permits slight mismatches in qualified pointer types is not applied recursively, but only at the top level. (const char ** is pointer-to-pointer-to-const-char, and the exception therefore does not apply.)
The reason that you cannot assign a char ** value to a const char ** pointer is somewhat obscure. Given that the const qualifier exists at all, the compiler would like to help you keep your promises not to modify const values. That's why you can assign a char * to a const char *, but not the other way around: it's clearly safe to ``add'' const-ness to a simple pointer, but it would be dangerous to take it away. However, suppose you performed the following more complicated series of assignments:
const char c = 'x'; /* 1 */
char *p1; /* 2 */
const char **p2 = &p1; /* 3 */
*p2 = &c; /* 4 */
*p1 = 'X'; /* 5 */
In line 3, we assign a char ** to a const char **. (The compiler should complain.) In line 4, we assign a const char * to a const char *; this is clearly legal. In line 5, we modify what a char * points to--this is supposed to be legal. However, p1 ends up pointing to c, which is const. This came about in line 4, because *p2 was really p1. This was set up in line 3, which is an assignment of a form that is disallowed, and this is exactly why line 3 is disallowed.
Assigning a char ** to a const char ** (as in line 3, and in the original question) is not immediately dangerous. But it sets up a situation in which p2's promise--that the ultimately-pointed-to value won't be modified--cannot be kept.
(C++ has more complicated rules for assigning const-qualified pointers which let you make more kinds of assignments without incurring warnings, but still protect against inadvertent attempts to modify const values. C++ would still not allow assigning a char ** to a const char **, but it would let you get away with assigning a char ** to a const char * const *.)
In C, if you must assign or pass pointers which have qualifier mismatches at other than the first level of indirection, you must use explicit casts (e.g. (const char **) in this case), although as always, the need for such a cast may indicate a deeper problem which the cast doesn't really fix.
Why do I need to change the print_args call in this code to print_args(argc, (const char**)argv) to make it compile?
#include <stdio.h>
void print_args(int argc, const char *argv[]) {
for (int i = 0; i < argc; ++ i) {
puts(argv[i]);
}
}
int main(int argc, char *argv[]) {
print_args(argc, argv);
return 0;
}
When I compile it with gcc I get this error:
$ gcc -Werror -std=c99 -g const.c -o const
const.c: In function ‘main’:
const.c:10:2: error: passing argument 2 of ‘print_args’ from incompatible pointer type [-Werror]
const.c:3:6: note: expected ‘const char **’ but argument is of type ‘char **’
cc1: all warnings being treated as errors
(Note that this is just a simplified example code to illustrate the problem.)
The comp.lang.c FAQ has a question on this, summarizing:
The reason that you cannot assign a char ** value to a const char **
pointer is somewhat obscure. Given that the const qualifier exists at
all, the compiler would like to help you keep your promises not to
modify const values. That's why you can assign a char * to a const
char *, but not the other way around: it's clearly safe to "add"
const-ness to a simple pointer, but it would be dangerous to take it
away...In C, if you must assign or pass pointers which have qualifier
mismatches at other than the first level of indirection, you must use
explicit casts
Here's the example it gives, where line 3 should give a warning:
const char c = 'x'; /* 1 */
char *p1; /* 2 */
const char **p2 = &p1; /* 3 */
*p2 = &c; /* 4 */
*p1 = 'X'; /* 5 */
Lines 1 and 2 are obviously fine. On line 4, &c has type pointer to const char, and since char ** is type pointer to pointer to const char, then *p2 is also of type pointer to const char, so the statement is fine. Line 5 is also fine, because p1 is type pointer to char, so you can modify what it points to.
So if line 3 is OK, then you end up doing exactly what you expect const to prevent you from doing, modifying the char pointed to by a pointer to pointer to const char, so line 3 is not allowed, and you can't assign char ** to a const char ** without a cast.
Note that putting the cast in merely hides the problem, it doesn't fix it. Line 3 above would work if you added the cast, and you'd be able to indirectly modify an apparently const char. While it's not always true, this is a good example of where succumbing to an apparent need to cast in C is often just a mistake.
Note that in C++, you could declare your function parameter const char * const * argv and it would work without a cast, and prevent the indirect modification.