Consider this program:
int func2(char x, char y)
{
return x+y;
}
int (*fp1)(char);
void main()
{
fp1 = func2; /* func2 has one more argument than fp1 */
}
Is the final assignment C90-compliant?
Keil C51 v9.06 accepts the program without warnings, while gcc complains with
warning: assignment from incompatible pointer type
I would like to know if this a bug in the Keil compiler or in fact a C90 compliant way to deal with this.
UPDATE: According to this answer on C99, the corresponding cast is legal. But if you invoke the function pointer you get undefined behavior. Does this mean that the assignment is also legal?
The real question is WHY are you trying to do this?
As soon as you call fp1 when it pointing to func2, func2 will return garbage because argument 2 is not passed (it'll be some random value on the stack, or in a CPU register).
when you build call for function func2 using function pointer fp1, there will be only one value passed on the stack, But in actual, function2 will refer 2nd value on stack as well. So either program will give segmentation fault and fail. Or if does not fail 2nd argument will have garbage value.
Related
I was randomly experimenting with code and tried to assign printf to an integer variable, using different compilers. Different values were returned. What is the explanation for that?
#include <stdio.h>
int main ()
{
int x;
x = printf;
printf ("%d", x);
return 0;
}
Value returned on my compiler is 4195360.
Look at the warnings from your compiler. (If your compiler doesn't show a warning for this code, look up how to configure it to show useful warnings.)
a.c:5:5: warning: incompatible pointer to integer conversion assigning to 'int'
from 'int (const char *, ...)' [-Wint-conversion]
x = printf;
^ ~~~~~~
1 warning generated.
printf is a function. Every function can be used as a pointer to that function. A pointer to a function is a way to refer to the function that can be placed into a variable. Typically, it's the address of the code of the function in memory.
It's possible to assign a pointer to an integer. The resulting value depends on your system. The integer is typically the address of the code of the function in memory.
There's nothing useful you can do with this value except in some very particular circumstances (such as crafting binary exploits, or managing dynamic code loading). Anything you can do with the value would be very specific to a particular hardware and software environment.
in the following code in file func.c:
#include <stdio.h>
int Myfunc1(int i, int z)
{
return i;
}
int main()
{
int ans;
/* casting the function into an 'int (int)' function */
ans = ((int(*)(int))(Myfunc1))(5);
printf("ans: %d\n\n", ans);
return 0;
}
i tried to cast an int(int,int) function into an int(int) function an got the gcc warning and note:
func.c:13:32: warning: function called through a non-compatible type [enabled by default]
func.c:13:32: note: if this code is reached, the program will abort
and when trying to run i get:
Illegal instruction (core dumped)
but if i compile this file with a .cpp ending with the gcc compiler it works OK.
can anyone explain the problem of the compiler in the .c case?
GNU GCC recognises all of the following as C++ files, and will use C++ compilation regardless of whether you invoke it through gcc or g++: .C, .cc, .cpp, .CPP, .c++, .cp, or .cxx
From https://stackoverflow.com/a/1546107/1767861
In that case, gcc compiles it in c++, which seems to accept the cast. Why is that ? See https://stackoverflow.com/a/559671/1767861
The problem is your signature for Myfunc1 and the function pointer you try to cast it to are of incompatible types. Instead, you need to do this:
ans = ((int(*)(int, int))(Myfunc1))(5, 5);
The link Thomas Ruiz posted explains why it is undefined behavior.
In summary:
Annex J.2
The behavior is undefined in the following circumstances:
-- A pointer is used to call a function whose type is not compatible with the pointed-to type (6.3.2.3).
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 pointed-to type, the
behavior is undefined.
#include <stdio.h>
int Myfunc1(int i, int z)
{
return i;
}
int main()
{
// define a function pointer and initialize it to NULL
int (*ans)(int, int) = NULL;
// get function pointer from function 'Myfunc1'
ans = Myfunc1(5, 6);
// call function using the pointer
printf("ans: %d\n", (*ans));
return 0;
}
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Casting a function pointer to another type
Assume i initialize a function pointer with a function that actually takes less parameters then the function pointer definition, will the function still perform correctly if called through the function pointer?
I tried this with gcc and it worked as expected, but i wonder if that behaviour is consistent across compilers/platforms (i suspect in some enviroments it might wreak havoc on the stack):
#include <stdio.h>
typedef void (*myfun)(int, int, int);
void test_a(int x, int y, int z) {
printf("test_a %d %d %d\n", x, y, z);
}
void test_b(int x, int y) {
printf("test_b %d %d\n", x, y);
}
int main() {
myfun fp;
fp = test_a;
fp(1, 2, 3);
fp = (myfun) test_b;
fp(4, 5, 6);
}
It is undefined behavior. Use at your own risk. It has been rumored to cause Nasal Demons!
The behavior of your program is undefined. The fact that it compiles at all is because of the cast, which effectively tells the compiler "this is wrong, but do it anyway". If you remove the cast, you'll get the appropriate error message:
a.c:17:8: error: assignment from incompatible pointer type [-Werror]
(From gcc -Wall -Werror.)
More specifically, the behavior depends on the calling conventions. If you were on a platform were the arguments were passed in "reverse" order on the stack, the program would give a very different result.
The function call is undefined behavior.
(C99, 6.3.2.3p8) "[...] If a converted pointer is used to call a function whose type is not compatible with the pointed-to type, the behavior is undefined."
For information note that a function type:
(C99, 6.2.5p20) "[...] describes a function with specified return type. A function type is
characterized by its return type and the number and types of its parameters."
Whether it works will depend on the calling convention being used.
I wouldn't recommend it.
I don't understand why the following code generates a warning:
int func(double a, int b, char c)
{
return 0;
}
int main()
{
int(*myPointer)() = func;
return 0;
}
I thought that in C, a function with an empty parameters list mean a function that can receive an unknown number of parameters. func4 happens to receive 3 parameters. So why is it incompatible with myPointer?
Its especially confusing because the following does compile without warning:
void boo()
{
return;
}
int main()
{
int(*pointerDude)(double) = boo;
return 0;
}
What's going on?
The difference between the two cases is explained like this: if you pass a parameter to a function that accepts none, you are just consuming some memory on the stack. If you don't pass any parameter to a function that accepts a few, the latter is going to read random data from the stack.
Update: changed to community wiki to add these corrections from Pascal Cuoq:
Casts of function pointer in both directions are legal, informative warnings from the compiler notwithstanding. Calling a function with the wrong prototype is illegal in both cases. Some ABIs mandate that the function clean up the stack, in which case passing parameters to functions that accept none corrupt the stack as surely as not passing parameters to functions that expect them.
C99 6.3.2.3 par. 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 pointed-to type, the
behavior is undefined
This is the warning you get:
warning: initialization from incompatible pointer type
You are passing void which means that will require a cast. You should use the right types.
IIRC C compiler has certain assumptions which can cause very odd bugs because you're not telling it how much stack space it needs whenever that function is called. Especially since the definitions don't match this might allow someone to inadvertently read your function pointer declaration and assume no parameters and have the function return garbage. If you really want to take variable numbers of parameters use my_func(...) and varargs.
A warning is just that, a warning. Honest.
In that case, the compiler knows you have an incorrect function pointer type and it blatantly tells you that. If you try to pass a number of parameters different then 3, then it will be issued an error.
I think that the problem is related to the casting of the parameters, because char is not 32 bits. Note that if you change the char to int this works fine. It is related to automatic promotion of variables.
Compare the two:
int func();
int func(double a, int b, int c)
{
return 0;
}
and
int func();
int func(double a, int b, char c)
{
return 0;
}
gcc gives you warning for the second, but not for the first
In C an empty parameter list means that the function does not take any parameters. I believe you are confusing an empty parameter list with varargs.
Changing your first example to include the parameters will remove the warning.
int func(double a, int b, char c)
{
return 0;
}
int main()
{
int(*myPointer)(double, int, char) = func;
return 0;
}
I was going to use dlopen, and dlsym on linux to make these two source files work:
#include <dlfcn.h>
#include <stdio.h>
int main()
{
int *(func)(void);
func=dlsym( dlopen("/home/noah/tmp/libmod.so.1", RTLD_LAZY), "func");
printf("%d\n", *func());
return 0;
}
and:
int func()
{
return 42;
}
but when I compile the first one, it keeps saying:
main.c:9: error: lvalue required as left operand of assignment
edit:
I tried adding a cast, and making it a function pointer, but now it says:
main.c:(.text+0x1f): undefined reference to dlopen'
main.c:(.text+0x2b): undefined reference todlsym'
Your declaration of func is confused:
int *(func)(void);
is equivalent to:
int *func(void);
so you're just giving the compiler a prototype for func without declaring a variable; the error occurs because a function is not a valid lvalue; however, a pointer to a function is a valid lvalue so you want this:
int (*func)(void);
And then your printf should be this:
printf("%d\n", func());
You'll also need to cast the return from dlsym to be strictly conforming to standard C:
func = (int (*)(void))dlsym(dlopen("/home/noah/tmp/libmod.so.1", RTLD_LAZY), "func");
A void* pointer can be silently upgraded to any other pointer type except a pointer to a function; gcc -pedantic, for example, will warn that "ISO C forbids assignment between function pointer and ‘void *’" without the cast. I don't have a copy of the standard handy (but someone around here certainly does) so I can't quote chapter and verse but caf is correct on this point (thanks caf).
And you also want to bookmark cdecl.org.
you can't really do what you are attempting
but it should be int (func*)()
and the printf should be func()
but you can't assign the dlsym with all its parameters as a function pointer
hint.... the function pointer "func" is just a pointer, it has no state, its just a memory address
and you shouldn't provide a function called func..... as you are making a pointer called func. you could make a function "int test_function(){ return 42; }
then in main go func = test_function; just to test using the function pointer
There is no need to *func()
printf("%d\n", func());
will be ok, cause func() will return the int, and you try to get dereference (*) of integer
look at precedence of operators (link)