Conversion between function pointers with void and non-void pointer parameters - c

I have a question related to void pointer conversions. Instead of casting between void* and non-void pointers, my question is about casting between function pointer types, one of which has void* as parameter type and another has a pointer to some particular data type.
Here's the code which allows to reproduce the warning messages:
#include <stdio.h>
typedef void (*module_outputMessage)(void *param, const char *msg);
void module_function(module_outputMessage outputFunc, void *output_param, int msgid, const char *msg1, const char *msg2)
{
if (msgid == 0)
outputFunc(output_param, msg1);
else
outputFunc(output_param, msg2);
}
struct main_state
{
int msgid;
};
void main_outputMessage(struct main_state *state, const char *str)
{
printf("Message %d: %s\n", state->msgid, str);
state->msgid++;
}
int main(int argc, char *argv[])
{
struct main_state state;
const char *msg1 = "abc", *msg2 = "def";
state.msgid = 0;
module_function(&main_outputMessage, &state, 0, msg1, msg2);
module_function(&main_outputMessage, &state, 0, msg1, msg2);
module_function(&main_outputMessage, &state, 1, msg1, msg2);
module_function(&main_outputMessage, &state, 0, msg1, msg2);
module_function(&main_outputMessage, &state, 1, msg1, msg2);
return 0;
}
This is it, the program is made of two parts, main and module. module outputs the text, but it shouldn't deal with all output specifics - instead, main is the one to handle the output. Since main is dependent on module and not vice versa, module does not know what's going on in main, and to output the messages, it needs an output function to be passed as a parameter. In order for output to know which state object it's dealing with, that object needs to be passed along with the output function. And that's where conversion comes into play: module doesn't know and shouldn't care about the implementation of main, so instead of using struct main_state* as function parameter, it accepts void* which it merely passes to output function.
So it all boils down to conversion between these types:
void (*)(void* , const char*)
void (*)(struct main_state *, const char*)
The program gives the expected results:
Message 0: abc
Message 1: abc
Message 2: def
Message 3: abc
Message 4: def
However, GCC complains about incompatible pointer types (I get five messages like this, one for each function call):
funcpointvoid.c: In function ‘main’:
funcpointvoid.c:33:2: warning: passing argument 1 of ‘module_function’ from incompatible pointer type
module_function(&main_outputMessage, &state, 0, msg1, msg2);
^
funcpointvoid.c:5:6: note: expected ‘module_outputMessage’ but argument is of type ‘void (*)(struct main_state *, const char *)’
void module_function(module_outputMessage outputFunc, void *output_param, int msgid, const char *msg1, const char *msg2)
^
So even though it works fine for me, with these warnings I'm not sure if this 'architecture' can be relied upon. But as I see it myself, the only difference is pointers to void and non-void, and it's just one way to use generic pointers for whatever purpose they exist. Is this a bug of GCC or have I missed something?

void (*)(void* , const char*)
void (*)(struct main_state *, const char*)
are two different types and as there are no implicit conversion between function pointer types you need to make the conversion explicit by using a cast:
Change:
module_function(&main_outputMessage, &state, 0, msg1, msg2);
to
module_function((module_outputMessage) main_outputMessage,
&state, 0, msg1, msg2);
but be aware that the function call outputFunc technically invoke undefined behavior as void (*)(void* , const char*) and void (*)(struct main_state *, const char*) are not compatible types.

Related

Convert to Pointer Argument in Function (C)

There is a function that takes the following argument :
int send_message(const char *topic)
I have a struct :
typedef struct mqtt_topic {
char topic[200];
} mqtt_topic_t;
and a value that is of the type : mqtt_topic_t *mqtt_topic
I am trying to pass mqtt_topic->topic as an argument to the function but it throws an error. How do I convert this data to useful format that I can then use as an argument in my function?
Here is the code snippet :
int mqtt_publish(char message[])
{
int msg_id = 0;
ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
mqtt_topic_t *mqtt_topic = get_mqtt_topic();
msg_id = esp_mqtt_client_publish(client,&mqtt_topic->topic, message, 0, 1, 0);
ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
return msg_id;
}
Function Prototype :
int esp_mqtt_client_publish(esp_mqtt_client_handle_t client, const char *topic, const char *data, int len, int qos, int retain);
The argument &mqtt_topic->topic has type "pointer to char[200]". What you want is pointer to just char:
msg_id = esp_mqtt_client_publish(client, mqtt_topic->topic, message, 0, 1, 0);
When passed as argument, or used in almost any other way except with & and sizeof operators, array decays to pointer to its first element. This is why mqtt_topic->topic gives char*, which is ok as const char* parameter needed here.
you do not need & in front of mqtt_topic->topic.
If your code does not compile (it is giving errors - not warnings) it means that you use a C++ compiler instead of C compiler or you set warning to be treated as errors.

passing arg 2 of --- discards qualifiers from pointer target type

I am having the following problem:
void send_Msg(const char* msg)
{
#if channel_free
(void) din_send_msg(channel, msg); // (void) rcd_send_msg(int channel, char* msg);
#else
(void) cin_sendMsg(channel, msg);
#endif
(void) din_send_msg(channel, msg);
gives me the warning "Passing arg 2 of 'din_send_msg' discards qualifiers from pointer target type".
I know that is because din_send_msg takes an int and a char* as parameters and it makes my const char* to a char*. Changing (void) din_send_msg(int channel, char* msg); to (void) din_send_msg(int channel, const char* msg); and assigning the const char* to a local pointer didn't do anything. But there's a good possibility, that I just did it wrong.
How can I deal with this warning?
(And yes, I absolutely have to get rid of it, even though it is just a warning)
What you need to do is, if the function din_send_msg is yours, and it is certain that it does not modify characters pointed to by the pointer is to change the prototype of the function everywhere and throughout to use
returntype din_send_msg(int channel, const char *msg);
I do not know what the actual return type of the function is.
If you can't do that, then you can usually use a cast to silence the warning:
din_send_msg(channel, (char *)msg);

C issues with pointers

I'm learning about the pointers in C. I don't understand why this code fails during the compilation process.
#include <stdio.h>
void set_error(int *err);
int main(int argc, const char * argv[])
{
const char *err;
set_error(&err);
return 0;
}
void set_error(int *err) {
*err = "Error message";
}
You declare the function to expect a pointer-to-int (int *). But you give it a pointer-to-pointer-to-char and set_error treats it as such. Change the declaration thusly:
void set_error(const char ** err)
If you had compiled with warnings enabled (-Wall for GCC) it would give the following warnings:
In function 'main':
warning: passing argument 1 of 'set_error' from incompatible pointer type [enabled by default]
set_error(&err);
^
note: expected 'int *' but argument is of type 'const char **'
void set_error(int *err);
^
In function 'set_error':
warning: assignment makes integer from pointer without a cast [enabled by default]
*err = "Error message";
^
Your function expects int * type argument but you are passing to it const char ** type argument.
Change your function declaration to
void set_error(const char **err);
The issue you have unearths an important facts about strings in C.
It also raises an interesting fact about scoping.
1. There is no such thing as a string in C; only a pointer to an array of characters.
Therefore, your statement *err = "Error message"; is wrong because by derefencing err you're not getting to the value of the string, but it's first character. (You can't quantify the 'value of a string' in C because there's no such thing as a string in C)
*err is actually undefined because nothing is yet assigned.
Note that the usual definition of a string is const char * or char * so I've changed this from what you had for the note below:
#include <stdio.h>
int main(void){
char * a = "hello";
if (*a == 'h'){
printf("it's an 'H'\n");
}
else{
printf("no it isn't\n");
}
}
You'll see that *err actually returns the value of the first character because a[0] == *a
2. You cannot return pointers to locally scoped data in C
set_error() has the correct intentions, but is doomed to fail. Although "Error message"looks like a value, it is actually already a pointer (because strings in C are pointers to character arrays, as mentioned above).
Therefore, taking (1) into account you might expect to be able to do this:
void set_int(int *myint) {
*myint = 1; //works just fine because 1 is a value, not a reference
}
void set_error(char *err) {
// doesn't work because you're trying to assign a pointer to a char
*err = "Error message";
void set_error_new(char *err) {
//doesn't work because when the function returns, "Error Message" is no longer available on the stack" (assignment works, but when you later try to get at that data, you'll segfault
err = "Error message";
}
You need to take a different approach to how you play with so-called 'strings' in C. Think of them as a pointer to a character array and you'll get better at understanding these issues. Also see C: differences between char pointer and array
One problem is that set_error expects an int * parameter, but you're passing the address of a char *, which makes it a char **. In addition, as noted by #Kninnug there's a buffer overwrite problem here which needs to be dealt with. Try rewriting your code as:
#include <stdio.h>
#include <string.h>
void set_error(char *err, size_t errbuf_size);
int main(int argc, const char * argv[])
{
char err_buf[1000];
set_error(err_buf, sizeof(err_buf));
printf("err_buf = '%s'\n", err_buf);
return 0;
}
void set_error(char *err, size_t errbuf_size) {
strncpy(err, "Error message", errbuf_size-1);
}
As you'll notice in the rewritten version of set_error, another problem is that you can't just assign a value to a pointer and have the target buffer changed - you need to use the string functions from the standard library (here I'm use strncpy to copy the constant "Error message" to the buffer pointed to by the char * variable err). You may want to get familiar with these.
Share and enjoy.
Firstly you have to change your function's declaration to
void set_error(char **err);
The body of the function is the same. Also you declared err variable as const char *err and tried change it. It generates a warning.
Let's start by talking about types. In your main function, you declare err as
const char *err;
and when you call the set_error function, you pass the expression &err, which will have type "pointer to const char *", or const char **.
However, in your function declaration and definition, you declare the parameter err as
int *err;
The types const char ** and int * aren't compatible, which is why the compiler is yakking. C doesn't allow you to assign pointer values of one type to pointer variables of a different type (unless one is a void *, which is a "generic" pointer type). Different pointer types are not guaranteed to have the same size or representation on a particular platform.
So that's where the compiler issue is coming from; what's the solution?
In C, string literals like "Error message" have type char *1 (const char * in C++), so whatever I assign it to needs to have a type of either char * or const char *. Since we're dealing with a string literal, the latter is preferable (attempting to modify the contents of a string literal invokes undefined behavior; some platforms put string literals in read-only memory, some don't). So you need to make the following changes to your code2:
void set_error( const char **err )
{
*err = "Error message";
}
int main( void ) // you're not dealing with command line arguments, so for this
{ // exercise you can use `void` for your parameter list
const char *err;
set_error( &err );
return 0;
}
Remember that C passes all function arguments by value; this means that the formal parameter err in set_error is a different object in memory than the actual parameter err in main; if the code had been
void set_error( const char *err )
{
err = "Error message";
}
int main( void )
{
const char *err;
set_error( err );
return 0;
}
then the change to err in set_error would not be reflected in the variable err in main. If we want set_error to modify the value of err in main, we need to pass set_error a pointer to err and dereference it in the function. Since the parameter err has type const char **, the expression *err has type const char *, which is the type we need for this assignment to succeed.
1. Actually, that's not true; string literals have type "N-element array of char", where N is the number of characters in the string plus the 0 terminator. However, for reasons that aren't really worth going into here, the compiler will convert expressions of array type to expressions of pointer type in most circumstances. In this case, the string literal "Error message" is converted from an expression of type "14-element array of char" to "pointer to char".
2. A function definition also serves as a declaration; I typically put the called function before the caller so I don't have to mess with separate declarations. It means my code reads "backwards" or from the bottom up, but it saves some maintenance headaches.
1st error--> You are noticing is due to the fact that your function expects a pointer to int and you are passing a pointer to const char
2nd error--> You dereferenced the pointer and inserted the value "Error Message" which is a string and you pointer was pointer to char.
3rd error--> set_error(&err); --> This statement is wrong as err itself stores an address so there is no need to put & putting & means you are passing the address of the pointer *err and not the address which it is holding. So try this.
include <stdio.h>
void set_error(const char* err[]); //Function Declaration
int main()
{
const char* err[1000];
set_error(err);
printf("%s",*err);
return 0;
}
void set_error(const char* err[])
{
*err = "Error Message";
}

recv() C error invalid conversion from char to int

I have some C code below:
char update[MAX_UPDATE_LEN];
int remoteLen;
char pholder;
pholder = recv(update,connectDescriptor,MAX_UPDATE_LEN,MSG_DONTWAIT); //error
remoteLen = atoi("pholder");
I keep getting the following errors:
client.cpp:849: error: invalid conversion from `char*' to `int'
client.cpp:849: error: initializing argument 1 of `ssize_t recv(int, void*,
size_t, int)'
client.cpp:849: error: invalid conversion from `int' to `void*'
client.cpp:849: error: initializing argument 2 of `ssize_t recv(int, void*,
size_t, int)'
What is causing these errors?
There are several issues with this code:
char update[MAX_UPDATE_LEN];
int remoteLen;
char pholder;
pholder = recv(update,connectDescriptor,MAX_UPDATE_LEN,MSG_DONTWAIT); <-- error here
remoteLen = atoi("pholder");
recv returns an ssize_t which is usually much bigger than a char. So you can't safely store the return code in pholder.
The arguments to recv() are in the wrong order. Here's the declaration: ssize_t recv(int sockfd, void *buf, size_t len, int flags);
atoi is being passed a string which is not a number "pholder". It expects a string like "12345". If ASCII-encoded numerals is what you expect, you can give update to atoi.
Bonus: use sizeof(update) instead of MAX_UPDATE_LEN for len -- that way if the type declaration or size of update changes, you should still get the expected value.
Here's how you might fix it:
char update[MAX_UPDATE_LEN];
const int flags = MSG_DONTWAIT;
const ssize_t ret = recv(connectDescriptor, update , sizeof(update), flags);
if (-1 == ret)
{
perror("recv");
exit(1);
}
const int remoteLen = strtol(update, update + sizeof(update), 0);

idiomatic C for const double-pointers

I am aware that in C you can't implicitly convert, for instance, char** to const char** (c.f. C-Faq, SO question 1, SO Question 2).
On the other hand, if I see a function declared like so:
void foo(char** ppData);
I must assume the function may change the data passed in.
Therefore, if I am writing a function that will not change the data, it is better, in my opinion, to declare:
void foo(const char** ppData);
or even:
void foo(const char * const * ppData);
But that puts the users of the function in an awkward position.
They might have:
int main(int argc, char** argv)
{
foo(argv); // Oh no, compiler error (or warning)
...
}
And in order to cleanly call my function, they would need to insert a cast.
I come from a mostly C++ background, where this is less of an issue due to C++'s more in-depth const rules.
What is the idiomatic solution in C?
Declare foo as taking a char**, and just document the fact that it won't change its inputs? That seems a bit gross, esp. since it punishes users who might have a const char** that they want to pass it (now they have to cast away const-ness)
Force users to cast their input, adding const-ness.
Something else?
Although you already have accepted an answer, I'd like to go for 3) namely macros. You can write these in a way that the user of your function will just write a call foo(x); where x can be const-qualified or not. The idea would to have one macro CASTIT that does the cast and checks if the argument is of a valid type, and another that is the user interface:
void totoFunc(char const*const* x);
#define CASTIT(T, X) ( \
(void)sizeof((T const*){ (X)[0] }), \
(T const*const*)(X) \
)
#define toto(X) totoFunc(CASTIT(char, X))
int main(void) {
char * * a0 = 0;
char const* * b0 = 0;
char *const* c0 = 0;
char const*const* d0 = 0;
int * * a1 = 0;
int const* * b1 = 0;
int *const* c1 = 0;
int const*const* d1 = 0;
toto(a0);
toto(b0);
toto(c0);
toto(d0);
toto(a1); // warning: initialization from incompatible pointer type
toto(b1); // warning: initialization from incompatible pointer type
toto(c1); // warning: initialization from incompatible pointer type
toto(d1); // warning: initialization from incompatible pointer type
}
The CASTIT macro looks a bit complicated, but all it does is to first check if X[0] is assignment compatible with char const*. It uses a compound literal for that. This then is hidden inside a sizeof to ensure that actually the compound literal is never created and also that X is not evaluated by that test.
Then follows a plain cast, but which by itself would be too dangerous.
As you can see by the examples in the main this exactly detects the erroneous cases.
A lot of that stuff is possible with macros. I recently cooked up a complicated example with const-qualified arrays.
2 is better than 1. 1 is pretty common though, since huge volumes of C code don't use const at all. So if you're writing new code for a new system, use 2. If you're writing maintenance code for an existing system where const is a rarity, use 1.
Go with option 2. Option 1 has the disadvantage that you mentioned and is less type-safe.
If I saw a function that takes a char ** argument and I've got a char *const * or similar, I'd make a copy and pass that, just in case.
Modern (C11+) way using _Generic to preserve type-safety and function pointers:
// joins an array of words into a new string;
// mutates neither *words nor **words
char *join_words (const char *const words[])
{
// ...
}
#define join_words(words) join_words(_Generic((words),\
char ** : (const char *const *)(words),\
char *const * : (const char *const *)(words),\
default : (words)\
))
// usage :
int main (void)
{
const char *const words_1[] = {"foo", "bar", NULL};
char *const words_2[] = {"foo", "bar", NULL};
const char *words_3[] = {"foo", "bar", NULL};
char *words_4[] = {"foo", "bar", NULL};
// none of the calls generate warnings:
join_words(words_1);
join_words(words_2);
join_words(words_3);
join_words(words_4);
// type-checking is preserved:
const int *const numbers[] = { (int[]){1, 2}, (int[]){3, 4}, NULL };
join_words(numbers);
// warning: incompatible pointer types passing
// 'const int *const [2]' to parameter of type 'const char *const *'
// since the macro is defined after the function's declaration and has the same name,
// we can also get a pointer to the function
char *(*funcptr) (const char *const *) = join_words;
}

Resources