Warning in Function pointer as a argument to another function - c

Just tried function pointer as an argument. Following code executed properly but throwing some warning. Not able to understand the warring. Can have some help here.
#include<stdio.h>
#include<string.h>
int concat(char *str1, char *str2, char *(*cat_fun) (const char *, const char *))
{
char str;
if(cat_fun) {
printf("%s\n",cat_fun (str1,str2));
return 0;
} else {
return 1;
}
}
int main(void)
{
char str1[20] = "super ";
char str2[] = "computer";
concat(str1,str2,strcat);
return 0;
}
Compilation:
gcc tttt.c
tttt.c: In function ‘main’:
tttt.c:22:2: warning: passing argument 3 of ‘concat’ from incompatible pointer type [enabled by default]
concat(str1,str2,strcat);
^
tttt.c:4:5: note: expected ‘char * (*)(const char *, const char *)’ but argument is of type ‘char * (*)(char * __restrict__, const char * __restrict__)’
int concat(char *str1, char *str2, char *(*cat_fun) (const char *, const char *))
^

This is the declaration of strcat:
char *strcat(char *dest, const char *src);
The first parameter is not a const pointer, because the function is supposed to modify that buffer. But your function pointer accepts that first parameter as a pointer to a const buffer, which is a promise that the pointee will not be modified. Naturally, that's a semantic incompatibility.
Strictly speaking, the C type system doesn't allow implicitly converting strcat to the pointer type you specify. But compilers may accept it in the interest of backward compatibility to older C versions, and emit only a warning. Bumping the standard compliance level in your build can help turn it into an error (and as such, maybe prevent nasty bugs down the line).

The first argument is char*, not const char*.
See here:
https://www.tutorialspoint.com/c_standard_library/c_function_strcat.htm

Related

Why I can't pass an argument of type 'char* const *' to a function which expected 'const char* const*' as it's argument? [duplicate]

This question already has answers here:
Double pointer const-correctness warnings in C
(3 answers)
expected expected ‘const char **’ but argument is of type ‘char **’
(1 answer)
Closed 2 years ago.
I am writing a function using fork and execv to replace system() and popen() in a project.
I found that although execv won't change the content of the command array, it declare it's second argument as char * const argv[], which can actually change the content of char*. Thus I decide to declare my function like this:int execute_cmd(const char* const cmd[]) so that both the pointer to char is const and the content the const pointer point to is const(Sorry I know this state is confusing but I tried)
However, when I call it in this way:
void execute_cmd(const char* const cmd[])
{
for (int i = 0; cmd[i]; i++)
{
printf("%s ", cmd[i]);
}
}
int main()
{
char* const cmd[] = {"ls", "-al", 0};
execute_cmd(cmd);
return 0;
}
there is a complie error saying that:
test.c:21: warning: passing argument 1 of ‘execute_cmd’ from
incompatible pointer type
test.c:10: note: expected ‘const char * const*’ but argument is of
type ‘char * const*’
I wonder why this happen because IMO an formal argument with const can take an non-const variable as it's actual parameter, such as:
void printInt(const int);
//which can call like this
int a=10;
printInt(a);
And I think initialize a const variable with a non-const variable is very common, So why this function work but my execute_cmd() didn't? Did I miss something importent with the const?
I have search "char * const * and const char* const *" and "const formal argument with non-const actual parameter" such things but haven't get something very useful, I will also appreciate if anyone can give me an idea about the correct key-work to this question.
I am not a native English speaker so if there are some grammar problem please forgive me.
I wonder why this happen because IMO an formal argument with const can take an non-const variable as it's actual parameter
It is more like "a formal parameter with const can take an non-const variable as it's actual argument."
const char* const cmd_paramter[] is like an "array of const pointer to const char".
char* const cmd_argument[] is an "array of const pointer to char".
Notice the type of the array elements differ: const char * vs. char *.
Same problem with simpler code: "expected 'char *' but argument is of type 'const char *'"
void foo(char *bob);
const char *alice = "Alice";
// Bad, as `foo()` needs to be able to write `*bob`, yet alice point to `const` data.
foo(alice);
Further, cmd array elements should be of type const char * to prevent writing a string literal.
// char* const cmd[] = {"ls", "-al", 0};
const char* const cmd[] = {"ls", "-al", 0};

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

Compiler claims pointer is const when it is not used

My compiler gives this warning:
inlinedata.h:9:6: note: expected ‘char *’ but argument is of type ‘const char *’
int inline_data_receive(char *data,int length);
I don't understand why it claims 'data' is a const pointer when it is not written as a const char*.
It's saying that the argument (the data that you are passing in) is const. It might be a string literal, for example. So instead of doing this:
ret = inline_data_receive("hello", len);
do this
char str[] = "hello";
ret = inline_data_receive(str, len);
You need to do it this way since the function is not guaranteeing that it won't modify the input string.
The compiler is complaining that you are passing a const char* value to a value that is labeled as char*. Essentially the following
const char* c = ...;
inline_data_receive(c, strlen(c));
The compiler is complaining that c is const char* but needs to be char* to line up with the argument data

C arrays of arrays: why do I need to cast TO const here?

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.

const char** parameter warning about char** argument

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.

Resources