Casting functions - c

I have the following functions:
typedef int (*Bar)(void *, int a);
int foo (Struct *s, int b);
void *call(Bar c);
What does it mean when I call the function:
call( (Bar) foo);

typedef int (*Bar)(void *, int a);
This defines Bar an alias for a pointer-to-function type, specifically for a function that takes two arguments of type void* and int and returns a result of type int.
int foo (Struct *s, int b);
This declares (but does not define) a function named foo that takes two arguments as specified.
I presume Struct is a typedef, but it's a poor name, easily confused with the struct keyword.
void *call(Bar c);
This declares a function call that takes an argument of type Bar and returns a void* result.
call( (Bar) foo);
foo is the name of the function declared earlier. In this context, it's treated as an expression of pointer-to-function type. This pointer value is converted to type Bar. Since `Bar is a pointer-to-function type, the conversion is well defined.
That converted pointer is then passed to a function named call. (call returns a void* result, but it's quietly ignored here; is that what you intended?)
We don't know what the call function does with its argument, so we can't tell what the behavior of this statement might be.
A conversion from one function pointer type to another, using a cast, is well defined, and converting back to the original type will yield the original value. Calling a function using a function pointer with the wrong type has undefined behavior. (You might be able to get away with it in some cases, but don't.)
Your call function could try to make a call via its parameter, but that call would have undefined behavior unless that parameter is converted back to the correct type.
There isn't enough information in your question to know what's going on, and the confusing names you've chosen don't help.

Related

In C, can you omit the parameter types of a function pointer when initializing it with the address of a declared function?

I'm trying to make sure I understand the syntax for function pointers in C. I know it's just a pointer to a specific type. That being said, why does the following code compile and print '10'?
(Note I'm using GCC 9.4.0 on Ubuntu 20.04)
void f(int);
void (*foo)() = f;
int main(){
foo(10);
return 0;
}
void f(int n){
printf("%d\n", n);
}
Shouldn't we at least get a compile time warning: initialization of 'void(*)()' with incompatible pointer type 'void(*)(int)'? Is foo getting initialized to void(*)(int) based on f?
If I change 'void f(int)' to 'void f(float)' I do get a warning. If I add 3 more int params to f e.g. void f(int, int, int, int) it still compiles and runs fine. If I make the last param a float, another warning.
The rules are the same for function pointer declarations as for function declarations ; and a pointer to function with empty parentheses parameter list is compatible with a pointer to prototyped function, if the return type matches.
In your example the issues are equivalent to:
void g();
int main()
{
g(10);
}
void g(int) { ..... }
The rules are: you can declare a function with empty parentheses and call it with arguments, but in the function call, certain conditions must be met and it is undefined behaviour with no diagnostic required if those conditions are not met.
One of the "certain conditions" is that you cannot have float as a parameter type in the function definition. Another is that the type and count of arguments must match the type and count of parameters in the function definition. To repeat, no diagnostic is required for any of this; your compiler is going above minimum requirements by warning about the incorrect parameter type float.
The type of the function pointer foo:
void (*foo)()
Is a pointer to a function taking an unspecified number of parameters and returning void. So a pointer to any function that has a return type of void (assuming no parameters are subject to promotion, i.e. char, short, float) is compatible with foo and can be assigned to it.
If you instead defined it like this:
void (*foo)(void)
This would generate an error for an incompatible pointer type, as foo points to a function that takes no arguments and returns void, and f does not match that.

C99: cast callbacks with different number of arguments

in the following example I make a CAST of a function without arguments in a pointer to a function that should receive an argument. Assuming that it gives the desired result, is it possible that this procedure causes some malfunction?
online test: https://onlinegdb.com/SJ6QzzOKI
typedef void (*Callback)(const char*);
Callback cb;
void inserisce_cb(void* c) {
cb=c;
}
void esegue_cb(){
cb("pippo");
}
void scriveTitolo(const char* titolo) {
Uart_Println(titolo);
}
void scriveTitolo2() {
Uart_Println("pluto");
}
void main(){
inserisce_cb(scriveTitolo);
esegue_cb();
inserisce_cb(scriveTitolo2);
esegue_cb();
}
Converting a pointer to a function to another pointer to a function is defined by the c standard, but using the resulting pointer to call a function with an incompatible type is not, per C 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.
The declaration void scriveTitolo2() { … } defines a function that does not have a parameter type list (it uses the old C style of an identifier list, with that list being empty) and that takes no arguments. A Callback pointer points to a function that has a parameter type list and takes a const char * argument. These are incompatible per C 2018 6.7.6.3 15:
For two function types to be compatible,… 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,…
Since they do not agree in the number of parameters, they are incompatible.
The above speaks only to the issue of converting from void (*)() to void (*){const char *) and using the result to call the function. There is a separate issue in that the function pointer is passed to inserisce_cb, which takes an argument of type void *, which is a pointer to an object type. The C standard does not define the behavior of converting a pointer to a function type to a pointer to an object type. To remedy this, inserisce_cb should be declared to take a pointer to a function type, such as void inserisce_cb(Callback c).
If scriveTitolo2 can be changed, then the compatibility issue can be resolved by changing it to take a const char * parameter that is unused, changing its definition to void scriveTitolo2(const char *).
(Note that it is preferable to declare scriveTitolo2 with the modern C style, as void scriveTitolo2(void) { … }, rather than without the void. This is unrelated to the question, as it would not make the function types compatible, but this format of declaration provides more information to the compiler in many circumstances.)
Additional thoughts to Eric's answer, which holds true for C99 as well:
If you call a function with an argument list not compatible to the function's parameter list, this is according to C99 §6.5.2.2 (6) undefined behavior.
It may work, depending on your compiler's ABI. There are compilers that let the called function clean up the stack, other compilers let the caller clean up. The former case will most likely crash, the latter ... who knows.
You can declare your scriveTitolo2 with an ignored parameter:
void scriveTitolo2(const char*) {
/* ... */
}
And everyone is happy: you and the compiler.

Is it safe to pass a void pointer to function that accepts a non-void pointer?

Very simply, is the following code safe/portable?
#include <stdio.h>
#include <stdlib.h>
int add(int *a, int *b)
{
return *a + *b;
}
int main()
{
int x = 2;
int y = 3;
void *ptr1 = &x;
void *ptr2 = &y;
fprintf(stdout, "%d + %d = %d\n", x, y, add(ptr1, ptr2));
return EXIT_SUCCESS;
}
I've compiled this with -Wall -Werror and -Wextra and received no warnings; it seems to run fine.
There's two things to consider:
C allows the implicit conversion from a void pointer to any other object pointer type. So it's syntactically okay to pass those arguments.
The type of the actual object being pointed to, and the type of the pointer the function expects, must satisfy strict aliasing constraints, otherwise your program is in undefined behavior land.
You are okay on both points. So your program is perfectly fine.
It is safe:
C99
A pointer to void may be converted to or from a pointer to any incomplete or object type. A pointer to any incomplete or object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer.
But you need to make sure your original pointer type is correct.
It's fine but by the skin of your teeth.
You are converting an int* to void* and that is converted back to int* as the pointer is passed by value into add.
If, for example, add took two pointers to double, say, then the behaviour of your code would be undefined.
It's "safe" in that the behavior is well defined. Having a function declared as accepting void * is normal when it needs to deal with many different types of data; e.g. memcpy.
It's not morally safe. By passing void * you are making the compiler unable to check that the pointer you are passing points to memory that holds data in the form and quantity that you expect. It is then up to you to enforce this.
In your trivial example you can see that the objects are actually int, and everything is fine.
In general, however, where we pass void * to a function, we should also pass additional information describing the memory we are handing over in enough detail that the function can do its job; if we have a function that only ever deals with one type, we look long and hard at the design decisions that made us pass around void * values instead of that type.
For sanity's sake (and, depending on the compiler, performance), please consider also marking arguments you are not writing to as const.
If you know that the pointers that you cast to void * are always to a known set of types (e.g. char, short, int, etc.), you can create a struct type consisting of a union together with a discriminant, and pass that instead, re-casting if necessary.
You can pass it as a pointer to an undefined type, just to be a bit more specific than void *, and in that way some responsibility is taken.

Declaration of function pointers

To declare a function pointer using typedef, it will be something like:
typedef void (*FOO)(int i)
But normally the syntax for typedef is like:
typedef int BOOL
so why is the first one not:
typedef void (*)(int) FOO
To return a function pointer (without typedef), the syntax is as follow:
void (*foo(char c))(int)
which means foo takes in a char and returns a pointer to a function which takes an int and returns nothing. The syntax is so weird! It looks like foo takes in an int and returns nothing. The parentheses don't seem to be in the right places. If it is to return a function pointer, why is it not something like:
(void (*)(int)) foo(char c)
which is very straightforward. I am really having trouble understanding the syntax here, let alone using it. Can someone please explain what's going on here?
An integer is just:
int x;
A name for the above is given by:
typedef int x_type;
A pointer to an int is:
int *p;
It's type would be:
typedef int *p_type;
A function called foo taking a double``and returning anint` is:
int foo(double);
Defining the type of foo would be:
typedef int foo_type(double);
Now a pointer to the above should take an *, but () (function call) binds tighter than * (dereference), so parentesis:
typedef int (*ptr_to_foo_type)(double);
This might be better written:
typedef foo_type *ptr_to_foo_type;
as some suggest writing for clarity.
The idea is that the type description looks (somewhat) like its use. Badly mangled idea, given prefix/postfix operators, that much everybody agrees on. But it is now much too late to change.
Declaration syntax is based on the types of expressions, not objects. Another way of saying it is that "declaration mimics use".
Let's start with a simple pointer expression; call it iptr. iptr points to an integer value. If we want to access that value, we need to dereference iptr with the unary * operator, like so:
x = *iptr;
The expression *iptr has type int, so the declaration of iptr is written
int *iptr;
If you wanted to create a typedef for an int pointer, you would add the typedef to get
typedef int *iptr;
iptr now serves as a synonym for type "pointer to int", so you can write
iptr ip;
which declares ip as a pointer to int (as a rule, you really don't want to hide pointers in typedefs).
Now let's say you have a pointer to a function that takes two int arguments and returns an int value, call it fp. To call the function, you dereference the pointer and pass the necessary arguments to the resulting function, like so:
x = (*fp)(arg1, arg2); // C allows you to omit the * in the call, so you could
// also write it as simply x = fp(arg1, arg2), but we're
// leaving it in so the following explanation makes sense
The function call () operator has higher precedence than unary *; *fp() will be interpreted as *(fp()), which is not what we want. To dereference fp before calling the function it points to, we must explcitly group the * operator with fp.
The type of the expression (*fp)(arg1, arg2) is int, so the declaration of fp becomes
int (*fp)(int arg1, int arg2);
Let's look at your second example now: foo is a function that takes a char argument and returns a pointer to a function that takes an int argument and returns void. You'd call it as something like
(*foo(c))(x);
Again, the type of the expression (*foo(c))(x) is void, so it follows that the declaration is
void (*foo(char c))(int x);
For syntactic reasons, typedef is treated as a storage class specifier like extern or static, although it has a very different meaning. It doesn't change the structure of a declaration; it just changes how that declaration is interpreted by the compiler. Adding typedef to the above, as in
typedef void (*foo(char c))(int x);
now creates the synonym foo for the type "function returning pointer to function returning void". It's no different from how simpler type definitions like
typedef int *iptr;
behave.
1) The syntax for typedef is to take a declaration of a variable of that type and put typedef in front of it.
2) The syntax for declarations echos the syntax for actually obtaining a value of that type. See my other recent answers regarding precedence of addressing operators (including function call), such as (*twod)[3] vs *(twod)[3] C Pointers ... I'd rather not repeat it all again.

How can I typedef a function pointer that takes a function of its own type as an argument?

Example: A function that takes a function (that takes a function (that ...) and an int) and an int.
typedef void(*Func)(void (*)(void (*)(...), int), int);
It explodes recursively where (...). Is there a fundamental reason this can't be done or is there another syntax? It seems to me it should be possible without a cast. I'm really trying to pass a dispatch-table but I could figure that out if I could just pass this one type.
You can wrap the function pointer in a struct:
struct fnptr_struct;
typedef void (*fnptr)(struct fnptr_struct *);
struct fnptr_struct {
fnptr fp;
};
I'm not sure if this is an improvement on casting. I suspect that it's impossible without the struct because C requires types to be defined before they are used and there's no opaque syntax for typedef.
It's impossible to do directly. Your only options are to make the function pointer argument accept unspecified arguments, or to accept a pointer to a structure containing the function pointer, as Dave suggested.
// Define fnptr as a pointer to a function returning void, and which takes one
// argument of type 'pointer to a function returning void and taking
// an unspecified number of parameters of unspecified types'
typedef void (*fnptr)(void (*)());

Resources