Behaviour of const qualified function in C - c

I was wondering if const qualifying function pointers makes any difference, since the only meaning i could think of is auto const-qualifying its parameters, which is of course not the case.
I created a little example file (test.c):
typedef void* vop(void*);
vop fn;
const vop cfn;
int main(void){
vop *p_fn = fn;
const vop *cp_fn = fn; // <- gives compiler warning
vop *p_cfn = cfn;
const vop *cp_cfn = cfn;
}
and ran
gcc -Wall -Wno-unused-variable -c test.c
which yields the following warning:
warning: initialization makes '__attribute__((const))' qualified function pointer from unqualified [-Wdiscarded-qualifiers]
So it is "ok" to assign a "pointer to const vop" to a variable of type "pointer to vop" which, if it was not a function pointer would yield something like:
warning: initialization discards 'const' qualifier from pointer target type [-Wdiscarded-qualifiers]
But now it warns for the opposite case. So the question arises: What is the difference between const qualified function pointers and those that are not const qualified?
Note: The cppreference has the following paragraph:
If a function type is declared with the const type qualifier (through the use of typedef), the behavior is undefined.
Is the warning i saw a result of that "undefined behaviour" or does this paragraph not apply in this case (and if not, in what case can it be applied)?

A function type cannot have any type qualifier on it, including const. Doing so it undefined behavior.
From section 6.7.3p9 of the C standard:
If the specification of an array type includes any type qualifiers, the element type is so-qualified, not the array type. If the specification of a function type includes any type qualifiers, the behavior is undefined.
This declares a const function type:
const vop cfn;
And this declares a pointer to a const function type:
const vop *cp_fn;
Both of which violate 6.7.3p9.

What is the difference between const qualified function pointers and those that are not const qualified?
const has the usual meaning - one can be modified, the const one may not. Example:
void something();
void something_else();
int main() {
void (*normal_pointer)() = something;
normal_pointer = something_else; // all fine
void (*const const_qualified_pointer)() = something;
const_qualified_pointer = something_else; // error
// for fun, let's aad typedef examples
// similar with a typedef, if you want to
typedef void functype();
functype *pnt = something;
pnt = something_else; // all fine
functype *const cpnt = something;
cpnt = something_else; // error
// note that if typedef is already a pointer... then it's already a pointer
typedef void (*functypepnt)();
functypepnt pnt2 = something;
pnt2 = something_else; // all fine
const functypepnt cpnt2 = something;
cpnt2 = something_else; // error
}
Is the warning i saw a result of that "undefined behaviour" or does this paragraph not apply in this case (and if not, in what case can it be applied)?
Yes. vop is a function type. const vop is undefined behavior. gcc issues a warning and ignores the qualifier.
You may want to const-qualify the pointer itself instead, not the pointed-to-type:
vop *const cp_cfn = fn;

Related

Can that function parameter really be pointer-to-const?

In the following C program, functions f, g, and h are essentially identical, however clang-tidy says that parameter p can be pointer-to-const in g and h (but not in f). My understanding is that p can't be pointer-to-const in any of them. Is that a false positive?
struct S { int *p; };
extern void x(struct S *s);
void f(int *p)
{
struct S s;
s.p = p;
x(&s);
}
void g(int *p)
{
struct S s = { .p = p };
x(&s);
}
void h(int *p)
{
struct S s = { p };
x(&s);
}
int main()
{
int a = 0;
f(&a);
g(&a);
h(&a);
}
Output from clang-tidy:
$ clang-tidy --checks=* a.c
Error while trying to load a compilation database:
Could not auto-detect compilation database for file "a.c"
No compilation database found in /home/wolfram or any parent directory
fixed-compilation-database: Error while opening fixed database: No such file or directory
json-compilation-database: Error while opening JSON database: No such file or directory
Running without flags.
2 warnings generated.
/home/wolfram/a.c:14:13: warning: pointer parameter 'p' can be pointer to const [readability-non-const-parameter]
void g(int *p)
^
const
/home/wolfram/a.c:20:13: warning: pointer parameter 'p' can be pointer to const [readability-non-const-parameter]
void h(int *p)
^
const
Tried with clang-tidy versions 10 and 11.
The following may be related: https://bugs.llvm.org/show_bug.cgi?id=41393
The tool is bugged. The intention of this warning is to likely to enforce "const-correctness", something that only matters when the pointer is actually de-referenced inside the function. You don't de-reference the pointers.
But more importantly, the C language requires this for simple assignment of pointers (6.5.1.6.1), emphasis mine:
the left operand has atomic, qualified, or unqualified pointer type, and (considering
the type the left operand would have after lvalue conversion) both operands are
pointers to qualified or unqualified versions of compatible types, and the type pointed
to by the left has all the qualifiers of the type pointed to by the right;
Making the pointer parameter const would turn s.p = p; and similar into constraint violations - invalid C. The rules of initialization follow the rules of simple assignment too, so the various functions behave identically.

return discards ‘const’ qualifier from pointer target type

I am sorry if this has been asked previously but i have tried all the solutions available but still getting this error. someone please explain the meaning of this error and also the solution to avoid this.
char *find_char( char const *source,char const *chars ){
char const *result=NULL;
for(int i=0;*(chars+i);i++){
for(int j=0;*(source+j);j++){
if(*(chars+i)==*(source+j)){
result=chars+i;
return result;
}
}
}
return result;
}
when compiling, the following error occurs:
6_1.c: In function ‘find_char’:
6_1.c:8:12: warning: return discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
return result;
^
6_1.c:12:9: warning: return discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
return result;
can constants cannot be returned?
Your title is a bit misleading, and probably you misinterpreted the warning, too:
warning: return discards ‘const’ qualifier from pointer target type
[-Wdiscarded-qualifiers] return result;
The function is declared as non-const, i.e. char *find_char(..., while result is declared as const, i.e. char const *result; Hence, statement return result would mean to return a pointer that is declared as const as being non-const, and thus the warning.
I'd suggest to define your function as const char *find_char(....
Since result is of type char const *, use const in the function signature to match it:
char const *find_char( char const *source,char const *chars )
The return type of the function find_char does not match the type of result. They are char * and char const * respectively.
Change the return type of the function find_char to char const *.
This relates to a well-known shortcoming in the C language, which I will discuss below. Presuming we want find_char to return a char * even though its inputs are const char *, you should use an explicit cast in the return statement:
return (char *) result;
With return result;, there is an implicit conversion from char * to the incompatible type const char *. The compiler warns because this could be a mistake. With the explicit cast, you show the compiler the conversion is intentional, and it is not likely to warn. (If it does, you can turn off that particular warning category.)
Regarding the shortcoming in C, consider the nature of find_char. It is similar to the C library routine strstr, which suffers from the same issue:
The caller may have a char * and want a char * as a result.
We would like the function parameters to be const char * to inform the compiler that the function does not alter the strings it is passed, because this may create some optimization opportunities.
The function returns a pointer into one of its input strings. So it internally has a const char * but must return a char *, so it must convert it.
In other words, we have a char *, and we want to tell the compiler “Ths function will accept a char *, and it will not change the content, but it will return a char * result.” To do this in C, it is necessary to declare the parameter to be const char * but to convert the result pointer to char * before returning it.

Function pointer as a const argument

Is it possible to pass a function pointer as a const argument?
I get the following gcc error:
warning: type defaults to 'int' in declaration of 'f'
[-Wimplicit-int] void execute(void (const *f)(void));
...when compiling this code:
#include <stdio.h>
void print(void);
void execute(void (const *f)(void));
int main(void)
{
execute(print); // sends address of print
return 0;
}
void print(void)
{
printf("const!");
}
void execute(void (const *f)(void)) // receive address of print
{
f();
}
This is not a duplicate of What is meaning of a pointer to a constant function?, which addresses pointers to const functions as opposed to a const function argument.
The syntax you want is:
void execute(void (* const f)(void));
This says that f is a const pointer to a function that takes no arguments ((void) is a special syntax for no arguments) and returns nothing (void). A const on the right side of an * says that the pointer is const. A const on the left side says the pointer points to something that is const.
The fact that it is a const pointer means that you will not change f (assign a new value) in the function, and the compiler should give you an error if you do (unless the change is hidden from it, which it can be by casts). It does not say anything about the function itself—the function pointed to is not const in any sense because the parameter is const.
When you wrote:
void execute(void (const * f)(void));
the const on the left side of the * means that the pointer was pointing to something that was const, rather than that the pointer itself was const. Since it was saying the pointer was pointing to something there and you did not list a specific type, the compiler warned you that the type was missing (and defaulted to int).
The const keyword should be placed between the pointer symbol (*) and the argument name (here, f):
void execute(void (* const f)(void))
{
f();
}
Now if you try to change f's value:
void execute(void (* const f)(void))
{
f = print;
f();
}
...your compiler should output an error similar to this one, as expected:
Error: cannot assign to variable 'f' with const-qualified type

Why does passing arrays of non-const members to functions receiving them as const generate compiler warnings? [duplicate]

The const modifier in C++ before star means that using this pointer the value pointed at cannot be changed, while the pointer itself can be made to point something else. In the below
void justloadme(const int **ptr)
{
*ptr = new int[5];
}
int main()
{
int *ptr = NULL;
justloadme(&ptr);
}
justloadme function should not be allowed to edit the integer values (if any) pointed by the passed param, while it can edit the int* value (since the const is not after the first star), but still why do I get a compiler error in both GCC and VC++?
GCC: error: invalid conversion from int** to const int**
VC++: error C2664: 'justloadme' : cannot convert parameter 1 from 'int **' to 'const int **'. Conversion loses qualifiers
Why does it say that the conversion loses qualifiers? Isn't it gaining the const qualifier? Moreover, isn't it similar to strlen(const char*) where we pass a non-const char*
As most times, the compiler is right and intuition wrong. The problem is that if that particular assignment was allowed you could break const-correctness in your program:
const int constant = 10;
int *modifier = 0;
const int ** const_breaker = &modifier; // [*] this is equivalent to your code
*const_breaker = & constant; // no problem, const_breaker points to
// pointer to a constant integer, but...
// we are actually doing: modifer = &constant!!!
*modifier = 5; // ouch!! we are modifying a constant!!!
The line marked with [*] is the culprit for that violation, and is disallowed for that particular reason. The language allows adding const to the last level but not the first:
int * const * correct = &modifier; // ok, this does not break correctness of the code

warning C4090: 'function' : different 'const' qualifiers

I am observing error "warning C4090: 'function' : different 'const' qualifiers " because of below line of code. Going through other similar questions on SO I understand (not 100 percent) it is because of
--> const char* EmployeeList[] and my declaration in qsort of EmployeeList
#define Elements(array) (sizeof(array)/sizeof((array)[0]))
const char *EmployeeList[] =
{
"Larry Page", "Sergy Brin", "Sundar Pichai", "Merrisa Mayer"
};
// called from main
SortEmployee(EmployeeList, Elements(EmployeeList));
int Compare(const void *elemA, const void *elemB)
{
...
}
void SortEmployee(const char *EmployeeList[], size_t EmployeeCount)
{
qsort(EmployeeList, EmployeeCount, sizeof(EmployeeList[0]), Compare);
}
However I am unable to resolve it- Any pointers how to do it for array of strings.
The problem is qsort does not declare its argument as const, while your code does. That means qsort may (in theory) change data, pointed by EmployeeList. So, the compiler reports this error.
Here is the official example: https://msdn.microsoft.com/en-us/library/k77bkb8d.aspx
How ever, here is a simple version to demonstrate my idea:
void foo(char* a) {
*a = '1'; // I got pointer to char, and changed this char!
}
int main() {
const char *a = "A"; // I have "CONSTANT POINTER": it points to CONSTANT memory, that CAN NOT be changed (by the way, string constants can't in many environments).
foo(a); // I pass it to my function, that will change it.
return 0;
}
Image your compiler stores a in read-only memory (It can, because we told it "this is a pointer to READ ONLY data"). You then modify it (in main function). Something bad may happen. So, the compiler warns you "hey, you pass a pointer to constant data to some function, that does not know that this data is constant and may change it"

Resources