Pointers can be declared as pointing to mutable (non-const) data or pointer to constant data.
Pointers can be defined to point to a function.
My coworkers and I were discussing the use of "const" with pointers and the question came up regarding the use of const with function pointers.
Here are some questions:
What is the meaning of a pointer to a constant function versus a
pointer to a non-constant function?
Can a function be const?
Can a function be non-const (mutable)?
What is the proper (safe) syntax for passing a function pointer?
Edit 1: Function pointer syntax
typedef void (*Function_Pointer)(void); // Pointer to void function returning void.
void function_a(Function_Pointer p_func); // Example 1.
void function_b(const Function_Pointer p_func); // Example 2.
void function_c(Function_Pointer const p_func); // Example 3.
void function_d(const Function_Pointer const p_func); // Example 4.
The above declarations are examples of treating a function pointer like a pointer to an intrinsic type.
A data, variable or memory pointer allows for the above combinations.
So the questions are: can a function pointer have the same combinations and what is meant by a pointer to a const function (such as Example 2)?
In C, there's no such thing as a function being const or otherwise, so a pointer to a const function is meaningless (shouldn't compile, though I haven't checked with any particular compiler).
Note that although it's different, you can have a const pointer to a function, a pointer to function returning const, etc. Essentially everything but the function itself can be const. Consider a few examples:
// normal pointer to function
int (*func)(int);
// pointer to const function -- not allowed
int (const *func)(int);
// const pointer to function. Allowed, must be initialized.
int (*const func)(int) = some_func;
// Bonus: pointer to function returning pointer to const
void const *(*func)(int);
// triple bonus: const pointer to function returning pointer to const.
void const *(*const func)(int) = func.
As far as passing a pointer to a function as a parameter goes, it's pretty simple. You normally want to just pass a pointer to the correct type. However, a pointer to any type of function can be converted to a pointer to some other type of function, then back to its original type, and retain the original value.
According to the C spec (C99, section 6.7.3):
The properties associated with qualified types are meaningful only for
expressions that are lvalues.
When the spec says "qualified types", it means things defined with the const, restrict, orvolatile keyword. Snice functions are not lvalues, the const keyword on a function isn't meaningful. You may be looking at some sort of compiler-specific extension. Some compilers will throw an error if you try to declare a function as const.
Are you sure that you're looking at a pointer to a constant function and not a constant pointer to a function (that is, it's the pointer that's const, not the function)?
Regarding #4: see this guide for a helpful overview of creating, passing, and using function pointers.
Under C, there is no such thing as a const function. const is a type-qualifier, and so can only be used to qualify a type, not a function. Maybe you mean a const pointer to a function or a non-const pointer to a function?
In C++, methods can be const. If a method is const, it means that after you call that method, the object containing the method will be in the same state as before you called the method (none of the instance variables[1] have been modified). Thus, you can point to a const method and a non-const method, and those methods are different.
You can accept a function pointer in an argument list as retType (*variableName)(arguments).
[1] Unless they are mutable.
In C, functions can be const if you are in the world of GCC! Functions can be declared const through the use of attributes attached to declarations of functions and other symbols. It is basically used to provide information to the compiler on what the function does, even though its body is not available so that the compiler can do some kind of optimizations with it.
A constant function is generally defined in terms of a pure function.
A pure function is a function with basically no side effect. This
means that pure functions return a value that is calculated based on
given parameters and global memory, but cannot affect the value of any
other global variable. Pure functions cannot reasonably lack a return
type (i.e. have a void return type).
And now we can define what is a const function,
A pure function that does not access global memory, but only its
parameters, is called a constant function. This is because the
function, being unrelated to the state of global memory, will always
return the same value when given the same parameters. The return value
is thus derived directly and exclusively from the values of the
parameters given.
Here const doesn't imply anything about function mutability. But it is a function which doesn't touch global memory. You can assign normal pointers to such functions. Anyways the code region will generally ( forgetting self modifying code for a while ) be RO and you cannot modify it through the normal pointer.
Read the full insightful article here.
So when it comes to GCC Constant functions we are talking about optimizations and not function mutability.
1.What is the meaning of a pointer to a constant function versus a pointer to a non-constant function?
There is no difference between const and non-const: the function itself is not modifiable
Note: In C++ if the function is a member function of a class, const means that the state of the object within this function cannot be changed (member variables assigned to, non-const memeber functions called). In this case the const keyword is part of the member function's signature and therefore makes a difference in terms of the pointer.
2.Can a function be const?
See above.
3.Can a function be non-const (mutable)?
See above
4.What is the proper (safe) syntax for passing a function pointer?
All pointers to free functions can be cast to any other pointer to free function (i.e. their size is the same). So you could define a type for a (hypothetical) function: void f(); and convert all function pointers to this type to store. Note that you should not call the function via this common type: you need to cast it bact to its original pointer-to-function type, otherwise you get undefined behavior (and most likely crash)
For C++: pointers to member functions are not guaranteed to be convertible to pointers to free functions
1.
There is syntactically nowhere to place 'const' to make a function's contents constant.
You will run into 'function is not an l-value' error regardless if you have const or not.
typedef void (* FUNC)(void);
FUNC pFunc;
pFunc = 0; // OK
*pFunc = 0; // error: Cannot assign to a function (a function is not an l-value)
typedef void (* const FUNC)(void);
FUNC pFunc;
pFunc = 0; // error (const)
*pFunc = 0; // error: Cannot assign to a function (a function is not an l-value)
typedef void (const * FUNC)(void); // error: <cv-qualifier> (lol?)
2 & 3.
Function pointers- yes.. Function contents, doesn't look like.
4.
I don't think there is any way to make passing a function pointer safer. With all the consts in the world, the only thing you can protect is that 'SetCallback' can't change it's own local copy of the parameter.
typedef void (* const FUNC)(void);
void SetCallback(const FUNC const pCallback)
{
FUNC pTemp = pCallback; // OK (even though pTemp is not const!!)
pCallback = 0; // error (const)
}
Related
I am currently reading on the ways of passing arguments to C functions. And while reading I came to know that in C there are two methods to pass arguments as pass by value and pass by reference.
Then again I read that we can pass a pointer to the variable as a parameter too.
The beginnersbook website this method is mentioned as pass by reference.
In this example, we are passing a pointer to a function. When we pass a pointer as an argument instead of a variable then the address of the variable is passed instead of the value. So any change made by the function using the pointer is permanently made at the address of passed variable. This technique is known as call by reference in C.
I know that we can pass a pointer variable or the address of the variable with & operator when we need to change or access the original variable. I want to know whether this pass by pointer can also be called pass by reference.
Is pass by pointer also a method of pass by reference?
Technically, all arguments to functions in C are pass by value. The language doesn't have support for true references unlike C++.
Passing by reference can be emulated in C by passing (by value) the address of the variable you want to modify, then subsequently dereferencing that address to modify the pointed-to value. This is not true pass by reference, however.
Pointers in C are references. C 2018 6.2.5 20 says:
… A pointer type describes an object whose value provides a reference to an entity of the referenced type…
When you pass a pointer to a function, you are passing the function a reference to whatever object the pointer points to. The pointer itself is passed by value, but, because the pointer is a reference and you are passing it, the description that you are passing an object by reference is accurate.
Prior to the development of C++, there was no dispute about this; people said they were passing an object by reference to describe passing a pointer to the object. C++ adopted the word “reference” as a name for a new feature in its language, so, in C++ terminology, “reference” generally refers to that feature unless otherwise stated or made apparent by context. However, in C, we are not obliged by C++ terminology, and the original meaning of pass by reference remains accurate.
In general, all arguments to functions in 'c' are passed 'by values'. It means that If an argument is modified in a function, this modification will not be visible by the caller:
void foo(int val) {
...
val = newval;
...
}
void bar() {
foo(4);
}
The value val in the above example can be modified and used in 'foo' but 'bar' will not see the update.
In order to make an update visible in the caller, pointers are used.
void foo(int *arg) {
...
*arg = 5;
...
}
void bar () {
int x = 4;
foo(&x);
// x will be 5 here
}
In the above case the address of 'x' will be passed to the function foo and it will be used inside the function *arg to modify the value of 'x'.
One can modify the value of the pointer inside the foo, since it itself is passed by value
void foo(int *arg) {
arg = newAddress;
}
but function 'bar' will not see this modification as well.
So, there are no real 'references' in 'c', however some people use it in reference to the passing of pointers.
I'm trying to learn c, so I tried reading some source code.
But I have no idea what this might mean:
static const char*(*const functab[])(void)={
ram,date
};
The first part, static const char* is fine, as it seems to be a function (has an argument of type void), static should mean that it is only visible in this file and const char* should mean that the value cannot be changed but the address can be changed.
But in that case, it doesn't make sense after the last part following the function name, as it was the case with
static const char * date(void);
static const char * ram(void);
Instead of the function name there is (*const functab[]), a const array called functab containing addresses?
Is this some kind of wrapping function containing the functions ram and date? Some alternative way of declaring arrays?
functab is an array of function pointers (array of const function pointers, to be exact), which returns const char* and accepts no arguments.
Later,
... = { ram, date };
is a brace-enclosed initializer list which serves as the initializer for the array.
This is the way to define array of function pointers in C. So instead of calling function as ram(), using this array you can call it by (* functab[1]).
Below discussion has good examples of array of function pointer:
How can I use an array of function pointers?
Short answer: Here functab is an array of function pointers, and the array is initialized with pointers to functions ram and date. This also explains the name functab which is likely from "FUNCtion TABle".
Long answer: In C you can get a pointer to a function and save it to a variable. A variable used in this fashion is known as a function pointer.
For example, funcptr variable below will contain the address of do_stuff's entry point:
int do_stuff(const char* x) { ...do stuff.. }
...
ftype_t funcptr = &do_stuff;
...
(*funcptr)("now"); // calls do_stuff()
This will only work if you have already defined the type of funcptr, which is ftype_t here. The type definition should take this form:
typedef int (*ftype_t)(const char*);
In English, this means ftype_t is being defined as a type of function that takes const char* as its only argument and returns int.
If you didn't want to typedef only for this, you could have achieved the same thing by doing below:
int (*funcptr)(const char*) = &do_stuff;
This works, but its syntax is confusing. Also it gets quite ugly if you attempt to do something like building an array of function pointers, which is exactly what your code does.
Shown below is equivalent code, which is much easier to understand:
typedef static const char*(*myfn_t)(void);
const myfn_t functab[] = { &ram, &date };
(The & (address of) is usually optional, but recommended. )
Complex variable declarations need to be read inside out in C:
functab is the identifier of the variable, so we start reading here...
functab[] it is an array...
*const functab[] of constant pointers...
(*const functab[])(...) to functions...
(*const functab[])(void) that take no arguments...
const char*(*const functab[])(void) but return a const char*.
The meaning of static depends on whether it's outside or inside of a function. If it's outside, the static means that functab is declared with file scope (i.e. a global variable that's only visible inside a single .c file). If it's inside a function, it means that functab is a global variable that's only visible inside that function.
The = { ram, date } initialize the array with two members. Both ram and date should be functions that are declared as const char* ram(void).
The effect of this declaration is, that the following function calls are equivalent:
const char* result = ram();
const char* result = functab[0]();
At least by the C11 standard and from what I've read.
The only place where return type is not allowed to be an array type is in the section of function definitions (at $6.9.1.3):
The return type of a function shall be void or a complete object type
other than array type.
At function calls ($6.5.2.2.1) it states this:
The expression that denotes the called function shall have type
pointer to function returning void or returning a complete object type
other than an array type.
Which means that something like this would be expected:
int (*pf1)()[4]; //legal?
pf1(); //error calling a function which return array
What I mean is that from how I understand the standard only defining a function returning arrays is illegal and not defining a pointer to function returning arrays. Prove me wrong if you can. Also if I'm wrong I would be happy if you explain me why is this sentence in the standard then?
Although clang doesn't seems to think that way and will rise an error in the above code stating that 'function cannot return array type 'int [4]''. But is this really a function (and not rather a pointer to one)?
EDIT:
OK - I was answered by citation of the standard paper that 'function declarators' can't have a return-type of array. However if we use a typedef name instead to declare a pointer to function returning arrays - would this be legal? -
typedef int arr_t[4];
arr_t (*pf1)(void);
Although I personally think that this case is also covered by the answers because the type-name defined by a 'typedef' is the same as one explicitly defined.
The sentence that you found is indeed only about function definitions, not about declarations. However, you missed another constraint:
6.7.5.3 Function declarators (including prototypes)
Constraints
1 A function declarator shall not specify a return type that is a function type or an array type.
Also if I'm wrong I would be happy if you explain me why is this sentence in the standard then?
There needs to be an additional requirement that a called function returns a complete object type because a function declaration is allowed to declare it as returning an incomplete type:
struct S;
struct S f(); /* valid */
void g() { f(); } /* invalid */
struct S { int i; };
void h() { f(); } /* valid */
It's not about arrays. The wording about "other than an array type" is just to make sure arrays don't accidentally become allowed by a mistake in the wording.
Declaring pointers to function returning arrays is actually legal?
No. Its not legal. You will get a compile time error. A function can't return an array.
For the time being, if int (*pf1)()[4]; is valid anyhow, then the function call
pf1();
doesn't make any sense. pf1 is not pointing to any function.
I was recently making some adjustments to code wherein I had to change a formal parameter in a function. Originally, the parameter was similar to the following (note, the structure was typedef'd earlier):
static MySpecialStructure my_special_structure;
static unsigned char char_being_passed; // Passed to function elsewhere.
static MySpecialStructure * p_my_special_structure; // Passed to function elsewhere.
int myFunction (MySpecialStructure * p_structure, unsigned char useful_char)
{
...
}
The change was made because I could define and initialize my_special_structure before compile time and myFunction never changed the value of it. This led to the following change:
static const MySpecialStructure my_special_structure;
static unsigned char char_being_passed; // Passed to function elsewhere.
static MySpecialStructure * p_my_special_structure; // Passed to function elsewhere.
int myFunction (const MySpecialStructure * p_structure, unsigned char useful_char)
{
...
}
I also noticed that when I ran Lint on my program that there were several Info 818's referencing a number of different functions. The info stated that "Pointer parameter 'x' (line 253) could be declared as pointing to const".
Now, I have two questions in regards to the above. First, in regards to the above code, since neither the pointer nor the variables within MySpecialStructure is changed within the function, is it beneficial to declare the pointer as constant as well? e.g. -
int myFunction (const MySpecialStructure * const p_structure, unsigned char useful_char)
My second question is in regards to the Lint information. Are there any benefits or drawbacks to declaring pointers as a constant formal parameter if the function is not changing its value... even if what you are passing to the function is never declared as a constant? e.g. -
static unsigned char my_char;
static unsigned char * p_my_char;
p_my_char = &my_char;
int myFunction (const unsigned char * p_char)
{
...
}
Thanks for your help!
Edited for clarification -
What are the advantages of declaring a pointer to const or a const pointer to const- as a formal parameter? I know that I can do it, but why would I want to... particularly in the case where the pointer being passed and the data it is pointing to are not declared constant?
What are the advantages of declaring a pointer as a const - as a formal parameter? I know that I can do it, but why would I want to... particularly in the case where the pointer being passed and the data it is pointing to are not declared constant?
I assumed you meant a pointer to const.
By have a pointer to const as a parameter, the advantage is you document the API by telling the programmer your function does not modify the object pointed by the pointer.
For example look at memcpy prototype:
void *memcpy(void * restrict s1, const void * restrict s2, size_t n);
It tells the programmer the object pointed to by s2 will not be modified through memcpy call.
It also provides compiler enforced documentation as the implementation will issue a diagnostic if you modify a pointee from a pointer to const.
const also allows to indicate users of your function that you won't modify this parameter behind their back
If you declare a formal parameter as const, the compiler can check that your code does not attempt to modify that parameter, yielding better software quality.
Const correctness is a wonderful thing. For one, it lets the compiler help keep you from making mistakes. An obvious simple case is assigning when you meant to compare. In that instance, if the pointer is const, the compiler will give you an error. Google 'const correctness' and you'll find many resources on the benefits of it.
For your first question, if you are damn sure of not modifying either the pointer or the variable it points to, you can by all means go ahead and make both of them constant!
Now, for your Qn as to why declare a formal pointer parameter as const even though the passed pointer is not constant, A typical use case is library function printf(). printf is supposed to accept const char * but the compiler doesn't complain even if you pass a char* to it. In such a case, it makes sense that printf() doesn't not build upon the user's mistake and alter user's data inadvertantly! Its like printf() clearly telling- Whether you pass a const char * or char*, dont worry, I still wont modify your data!
For your second question, const pointers find excellent application in the embedded world where we generally write to a memory address directly. Here is the detailed explanation
Well, what are the advantages of declaring anything as a const while you have the option to not to do so? After all, if you don't touch it, it doesn't matter if it's const or not. This provides some safety checks that the compiler can do for you, and it gives some information of the function interface. For example, you can safely pass a string literal to a function that expects a const char *, but you need to be careful if the parameter is declared as just a char *.
I have a function pointer inside a struct that gets dynamically set at runtime to the address of another function in various places in my code. It is defined in my header file like this:
void *(*run)();
During compile time, I get the following warning about this:
warning: function declaration isn't a prototype
This warning is benign, because the pointer is used in many places in my code to call the function it points to, and everything works just fine. However, I would really like to silence the warning.
If I change it to this:
void *(*run)(void);
I get compile errors whever I use it, because the various functions that make use of the pointer have different numbers of arguments, and saying void inside the parenthesies tells the compiler it accepts no arguments.
I can't use a va_list or anything fancy like that, as this is simply a pointer to another function, and I use a single pointer for them all because it keeps the code clean and simple.
I can silence the warning with adding this to my compiler flags:
-Wno-strict-prototypes
But I'd rather not have to disable compiler warnings with flags if I can avoid it.
So my question is: How do I notate this function pointer in the code in such a way that the compiler is satisfied with the fact that it accepts any number of any kind of arguments?
The code works perfectly. I just want the warning to go away.
Store the pointer as a void * and cast to the appropriate function pointer type when necessary? Keep in mind that it isn't necessarily safe to call one type of function pointer as if it were another type, so the warning you're starting out with isn't entirely invalid.
You can cast a function pointer like so:
void *genericPointer = ...;
void (*fp)(int, int) = genericPointer;
fp(123, 456);
Note that:
There's no explicit casting necessary here, as void * can always be cast to any pointer type.
The initial "void" before (*fp) is the return type of the function pointer.
You are trying to do things clean - i.e. involve the compiler in checks, but the design you invented simply cannot be clean by its principle. You cannot involve compiler in prototype checks this way, because you always must know, which parameters to pass at this particular case in runtime. Compiler cannot check this and if you make a mistake, segmentation fault is on the way.
But if I remember well, something like this was maybe used also in linux kernel (?). The solution is to have a general pointer (like the one you have) and each time you call a particular function you just typecast it to the pointer to function with the particular arguments. You may need to typecast it to void * first to silence the compiler again :-)
In C, when you call a function without a prototype visible, default argument promotions are applied to all of the arguments that you pass to the function. This means that the types that you actually pass do not necessarily match the types received by the function.
E.g.
void (*g)();
void f()
{
float x = 0.5;
g(x); // double passed
}
This means that you need to know that the function that you are actually calling has a compatible signature to that implied by the arguments that you are passing after promotion.
Given that you need to know this in any case you must know the function signature of the actual function being called at the call site which is using the function pointer. With this knowledge it is usually simpler and cleaner to use a function pointer with the correct prototype and you can avoid default argument promotion entirely.
Note that as you are defining your functions with prototypes, when you assigned a pointer to your function to a function pointer without a prototype you effective converted, say, a void(*)(int, int) to a void(*)() so it is completely correct and desirable to perform the reverse conversion before calling the function. gcc allows both these conversions without emitting any warnings.
E.g.
void PerformCall( void(*p)() )
{
if (some_condition)
{
// due to extra knowledge I now know p takes two int arguments
// so use a function pointer with the correct prototype.
void(*prototyped_p)(int, int) = p;
prototyped_p( 3, 4 );
}
}
Try typedefing the function pointer declaration and then have the caller explicityly cast it:
typedef void *(*run)();
//when calling...
void my_foo() {}
run r = (run)my_foo;
If the different function signatures are known, use a union. Otherwise, use a pointer of type void (*)(void) (actually, any function pointer type would do) to hold the generic pointer and convert to the proper type when setting the value and calling the code.
Example using a union:
union run_fn
{
void *(*as_unary)(int);
void *(*as_binary)(int, int);
};
struct foo
{
union run_fn run;
};
void *bar(int, int);
struct foo foo;
foo.run.as_binary = bar;
void *baz = foo.run.as_binary(42, -1);
Example using explicit casts:
struct foo
{
void (*run)(void);
};
void *bar(int, int);
struct foo foo;
foo.run = (void *(*)(int, int))bar;
void *baz = ((void *(*)(int, int))foo.run)(42, -1);
Don't use a void * to hold function pointers - such a conversion is unspecified by the ISO C standard and may be unavailable on certain architectures.
Ignoring the warning and using your code as-is is actually also a possibility, but keep in mind that any function argument will be subject to the default argument promotions and it's your responsibility that the promoted arguments properly match the declared parameters.