简体   繁体   中英

Passing `int (*)(char const*)` where `int (*)(char*)` is expected

I have a function pointer whose function is declared as expecting char * arguments.Into it, I'd like to save a pointer to a function declared as taking char const* arguments.

I guess I can either use a wrapper or a cast. Casts seem more straightforward, but can I legally call the result of such a function pointer cast?

Example code below:

static int write_a(char * X){
    return 0;
}

static int write_b(char const* X){
    return 0;
}
static int wrapped_write_b(char * X){
    return write_b(X);
}

typedef int (*write_fn)(char * );

write_fn a = write_a;
write_fn b = wrapped_write_b;
write_fn b1 = (write_fn)write_b; //is b1 legally callable?

This is undefined behavior - You can use a pointer to call a function of another type only if the types are compatible ( 6.3.2.3 /8 ):

A pointer to a function of one type may be converted to a pointer to a function of another type and back again; the result shall compare equal to the original pointer. If a converted pointer is used to call a function whose type is not compatible with the referenced type, the behavior is undefined.

Two functions have compatible types if (simplified version) they have same return and arguments are compatible ( 6.7.6.3 , Semantics/15 ):

For two function types to be compatible, both shall specify compatible return types.146) Moreover, the parameter type lists, if both are present, shall agree in the number of parameters and in use of the ellipsis terminator; corresponding parameters shall have compatible types.

A const char * is not compatible with a char * ( 6.7.6.1 , Semantics/2 ):

For two pointer types to be compatible, both shall be identically qualified and both shall be pointers to compatible types.

Since const char * and char * are not identically qualified, they are not compatible, and calling write_b through b is undefined behavior.

Strictly speaking, it is not allowed.

A pointer-to-something is not compatible with a pointer-to-qualified-something . Because a pointer-to-qualified-something is not a qualified type of pointer-to-something

The same applies for

pointer-to-function-accepting-something

and

pointer-to-function-accepting-qualified-something .

This can be found through C11 6.2.7 compatible type:

Two types have compatible type if their types are the same. Additional rules for determining whether two types are compatible are described in 6.7.2 for type specifiers, in 6.7.3 for type qualifiers...

Where 6.7.3 is the relevant part. It says

For two qualified types to be compatible, both shall have the identically qualified version of a compatible type;

The conversion chapter 6.3.2.3 does not contradict this:

A pointer to a function of one type may be converted to a pointer to a function of another type and back again; the result shall compare equal to the original pointer. If a converted pointer is used to call a function whose type is not compatible with the referenced type, the behavior is undefined.

EDIT

As noted in the answer by Holt, the compatibility of two functions is explicitly described in 6.7.6.3/15 .


I still think that a wrapper function is the best solution. The root of the problem is that write_a isn't const-correct. If you can't change that function, then write a wrapper around it.

write_fn b1 = (write_fn)write_b; //is this legal?

Is what legal?

Function pointer types involved in this cast are not compatible.

Yet casting function pointer to an incompatible function pointer type is perfectly legal. However, the only thing you can do with such forcefully converted pointer is convert it back to the original type. The language specification guarantees that such round-trip conversion will preserve the original pointer value. This is why we can use, say, void (*)(void) as a "universal storage type" for function pointers (like void * for data pointers). It can be used for storing function pointers of any type (but not for calling the functions). To perform such pointer storage (and retrieval) we'll have to use explicit casts, just like the one in your code. There's nothing illegal about it.

Meanwhile, trying to call the function through b1 will result in undefined behavior, specifically because the pointer type is not compatible with the actual function type.

In your question you clearly state that you want to "save" the pointer to that function. As long as we are talking only about "saving" (storing) the pointer, your code is perfectly flawless.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM