Note: I have seen assigning-function-to-function-pointer-const-argument-correctness. While it applies to C , the accepted answer seems to hint that this shouldn't be an issue.
I have compiled both with gcc
and g
and have the same issue. (g gives an error, and gcc a warning, but I assume this is due to different default flags).
A certain static analysis / linter tool we used at work (which shall remain anonymous) is causing this issue. Since the functions don't modify the pointer int* num
or the data itself, I should (must) declare the function args as const int* const num
.
typedef int (*funcptr)(int* num);
int func1( int* num) { return *num 1; }
int func2(const int* num) { return *num 1; }
int func3( int* const num){ return *num 1; }
int func4(const int* const num){ return *num 1; }
int main(const int argc, const char** const argv){
funcptr fptr;
fptr = func1; //No warning - Signature exactly matches funcptr
fptr = func2; //WARNING
fptr = func3; //No warning
fptr = func4; //WARNING
}
gcc minimal.c
minimal.c:13:10: warning: assignment to ‘funcptr’ {aka ‘int (*)(int *)’} from incompatible pointer type ‘int (*)(const int *)’ [-Wincompatible-pointer-types]
13 | fptr = func2;
| ^
minimal.c:15:10: warning: assignment to ‘funcptr’ {aka ‘int (*)(int *)’} from incompatible pointer type ‘int (*)(const int * const)’ [-Wincompatible-pointer-types]
15 | fptr = func4;
g minimal.c
minimal.c: In function ‘int main(int, const char**)’:
minimal.c:13:12: error: invalid conversion from ‘int (*)(const int*)’
to ‘funcptr’ {aka ‘int (*)(int*)’} [-fpermissive]
13 | fptr = func2;
| ^~~~~
| |
| int (*)(const int*)
minimal.c:15:12: error: invalid conversion from ‘int (*)(const int*)’
to ‘funcptr’ {aka ‘int (*)(int*)’} [-fpermissive]
15 | fptr = func4;
| ^~~~~
| |
| int (*)(const int*)
EDIT: I understand why the standard applies to the above code, thanks to the accepted answer. But why would the following be acceptable?
gcc minimal.c -Wall -Werror
int func(const int* const num){ return *num 1; }
int main(const int argc, const char** const argv){
int aNumber = 4;
int ret = func(&aNumber);
return ret;
}
CodePudding user response:
The function pointer funcptr
is NOT in fact compatible with func2
.
Two function types are compatible (ignoring cases including ...
or old-style parameter declarations) if their return types are compatible, they have the same number of arguments, and the arguments types are compatible. The first two cases are obviously satisfied, so the interesting part is the third case.
When type qualifiers are involved, two types are compatible if they are identically qualified versions of compatible types. By this definition, a const int *
and a int *
are not compatible. Therefore, funcptr
and func2
are not compatible.
While a pointer to a non-qualified type may be converted to a pointer to a qualified version of the same type, by the above rules this does not apply to function parameters.
The rules for compatible qualified types are spelled out in section 6.7.3p10 of the C standard:
For two qualified types to be compatible, both shall have the identically qualified version of a compatible type; the order of type qualifiers within a list of specifiers or qualifiers does not affect the specified type.
And the rules for compatible function types are listed in section 6.7.6.3p15:
For two function types to be compatible, both shall specify compatible return types. 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. If one type has a parameter type list and the other type is specified by a function declarator that is not part of a function definition and that contains an empty identifier list, the parameter list shall not have an ellipsis terminator and the type of each parameter shall be compatible with the type that results from the application of the default argument promotions. If one type has a parameter type list and the other type is specified by a function definition that contains a (possibly empty) identifier list, both shall agree in the number of parameters, and the type of each prototype parameter shall be compatible with the type that results from the application of the default argument promotions to the type of the corresponding identifier. (In the determination of type compatibility and of a composite type, each parameter declared with function or array type is taken as having the adjusted type and each parameter declared with qualified type is taken as having the unqualified version of its declared type.)
The parenthesized portion at the end is of particular note. It basically says that if a parameter is const
it is omitted for the purpose of determining compatibility. This is why func3
doesn't trigger a warning, since a parameter of type int * const
is equivalent in this context to int *
.
This does not apply to func2
because what the parameter points to is const
, not the parameter itself. So a parameter of type const int *
is not adjusted to int *
.