c语言中给定一个函数地址如何调用呢?

本文最后更新于:2022年6月8日 晚上

背景

在u-boot代码中有时会给出一个函数的地址(0x123456789xx),比如下面这种

然后告诉参数类型和返回值类型,那么我们要怎么使用呢?

首先先了解一下c语言中的函数

1
2
3
4
5
6
7
8
9
void fun() {
printf("fun call\r\n");
}

int main() {
printf("address fun:%p\r\n",fun);
printf("address fun:%p\r\n",&fun);
return 0;
}

编译运行结果如下,可以看到二者地址是一致的,但是其意义却大不相同

  • funfun函数的首地址,类型是 void ()
  • &fun表示指向函数fun这个对象的地址,类型是void (*)()

继续在了解一下函数指针类型

其本身是一个指针,只不过该指针指向函数。

其格式如:函数返回值数据类型 (*指针变量名)(函数的实际参数或者函数参数的类型),其中指针变量名可以省略。

形如void * (*f) (void *) ,意思是f是一个指针,其指向一个函数(可以是 void* fun(void *)等),函数的返回值为void *,函数的参数为void *

做一个简单的实验去验证一下,写了5个函数,其中

函数 返回值类型 参数类型
fun void 无参数
fun1 void (int )一个int型参数
fun2 void (int , int )两个int型参数
fun3 int (int ,char *)一个int型和一个char*型参数
fun4 int* (int *)一个int *型参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
void fun() {
printf("fun call\r\n");
}

void fun1(int a){
printf("fun1 call:%d\r\n",a);
}

void fun2(int a, char *b){
printf("fun2 call:%d %p\r\n",a,b);
}

int fun3(int a){
printf("fun3 call:%d\r\n",a);
return a;
}

int* fun4(int *a) {
*a += 10;
printf("fun4 call:%d\r\n",*a);
return a;
}

int main() {

printf("address fun:%p\r\n",fun);
printf("address fun:%p\r\n",&fun);
printf("address fun1:%p\r\n",&fun1);
printf("address fun2:%p\r\n",&fun2);
printf("address fun3:%p\r\n",&fun3);
printf("address fun4:%p\r\n",&fun4);

unsigned int add = (unsigned int )&fun;
unsigned int add1 = (unsigned int )&fun1;
unsigned int add2 = (unsigned int )&fun2;
unsigned int add3 = (unsigned int )&fun3;
unsigned int add4 = (unsigned int )&fun4;

printf("address fun:%x\r\n",add);
printf("address fun1:%x\r\n",add1);
printf("address fun2:%x\r\n",add2);
printf("address fun3:%x\r\n",add3);
printf("address fun4:%x\r\n",add4);

unsigned char dd = 4;
printf("address dd:%p\r\n",&dd);
int ff = 4;
printf("address ff:%p\r\n",&ff);
((void (*)())add)();
((void (*)(int ))add1)(10);
((void (*)(int ,char *))add2)(10,&dd);
printf("add3 return val:%d\r\n",((int (*)(int ))add3)(11));
printf("add4 return val:%d\r\n",*((int *(*)(int *))add4)(&ff));
return 0;
}

在代码中先打印指向函数fun*的地址,再将地址赋值给add*,其二者地址是一致的。

接着我们通过地址add*来调用函数fun*。先看add中存的是一个地址,其地址是fun的首地址,类型为(void (*)()),所以通过地址add*来调用函数时,首先将类型转为 (void (*)()),(void (*)())add,没有参数直接调用((void (*)())add)()

后面的也是类似,看下add4,其地址是fun4的首地址,类型为(int *(*)(int *) )(第一个int *表示返回值类型,(int *)表示参数类型为int *),所以通过地址调用时,先类型转化 (int *(*)(int *))add4,有函数参数和返回值,int *p = ((int *(*)(int *))add4)(&ff)。其结果和预期保持一致。

所以,使用函数地址完成函数调用时,首先转换类型,然后有参数传入参数,有返回值用对应类型来接

参考

  1. C/C++ 获取函数地址

c语言中给定一个函数地址如何调用呢?
https://www.glj0.top/posts/3b31c731/
作者
gong lj
发布于
2022年4月11日
更新于
2022年6月8日
许可协议