本文最后更新于: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; }
|
编译运行结果如下,可以看到二者地址是一致的,但是其意义却大不相同
fun
是fun
函数的首地址,类型是 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)
。其结果和预期保持一致。
所以,使用函数地址完成函数调用时,首先转换类型,然后有参数传入参数,有返回值用对应类型来接。
参考
- C/C++ 获取函数地址