今天在研究一位牛人写的代码,发现了一个以前没有注意过的东西,函数指针的类型强制转换。
指针的类型强转在C语言的开发中非常普遍,但是函数指针的类型强转我在之前的开发过程中极少使用,尤其是参数列表个数还不相同的函数指针,废话不多说,进入正题
首先定义两个函数指针类型,这个大家都懂的,这里先拿两个参数列表类型相同,但参数个数不同的函数指针进行指针类型强转,代码如下。
#include <stdio.h>
typedef void (*F1)(int, int);
typedef void (*F2)(int);
void ff1(int a, int b) {
printf("ff1\r\n");
printf("%d,%d\r\n", a, b);
}
void ff2(int a) {
printf("ff2\r\n");
printf("%d\r\n", a);
}
int main(int argc, char *argv[])
{
F1 f1 = (F1)ff2;
f1(1,2);
F2 f2 = (F2)ff1;
f2(3);
return 0;
}
运行结果如下:
从运行结果中可以看到,当函数ff2被强制转换成一个F1类型的函数后,ff2的形参a的取值为F1类型函数的第一个形参的值,这里说明程序在执行的时候,如果该函数为一个只有n个形参的函数,如果你传递给他m个形参(n<=m),则该函数只会使用前n个形参。至于这里为什么可以允许这样的操作,后面将试探的分析一下。
接着,我们试图把一个ff1类型的函数,强制转换为一个F2类型的函数,并执行,执行结果还是在上面的图中,可以看到函数ff1的运行结果为3,2。
问题来了,我明明只传递给ff1这个函数1个参数,但为什么编译器没报错?虽然类型经过了强制转换,也许可以骗过编译器,但是在执行的时候,明明少了一个参数,程序执行的时候为什么也不报错?而且第二个参数的值还比较诡异,从打印的情况看,第二个参数为2,该值正是调用ff2的时候传递的第二个参数,该参数没被ff2处理,这里却被ff1处理,神马情况?这里在网上查了一下,也没查到什么确切的证据,算了,自己分析一下吧。
首先,有一点,当每个函数被调用时,都被赋予了相对独立的栈空间,当该函数的生命周期结束后,该栈空间同时也被回收。
这样,当我们的main函数执行后,main函数有一个栈空间,假设为mem_A。那么根据上述实验结果,我这里有一个猜想,关于函数的参数是如何实现传递的。我的猜想是这样的,main函数在mem_A的栈空间中单独保留了一块类存给参数传递使用,假设该类存空间为mem_B。当一个函数运行时,程序把需要传递的参数按照顺序,放置在mem_B中,且每次都是从头开始放置。
回到刚刚的实验中,当ff2被执行后,mem_B中应该存放着0x00000001和0x00000002,这时再执行ff1,由于这里把ff1强制转换成了只含有一个形参的函数,所以当我们只传递给ff1一个参数3时,mem_B中应该存放着0x00000003和0x00000002,这样,ff1调用的结果可以符合以上输出结果,这里为了确定这样的结果不是巧合,又进行了数次实验,均与以上猜想相符。按照以上猜想,如果ff1先执行,ff2后执行,则ff1的第二个参数应该为一个随机数,为了验证该想法,又进行了如下实验。
int main(int argc, char *argv[])
{
F2 f2 = (F2)ff1;
f2(3);
F1 f1 = (F1)ff2;
f1(1,2);
return 0;
}
其他代码都一样,就是把两个函数的执行顺序掉了一下顺序,输出结果下
跟预期的也完全一样。
最后发现,参数的存储是以4字节为一个存储单元的,也就是说当一个char类型的变量作为参数传递,在mem_B中占的内存大写依旧为4字节,实验为证:
#include <stdio.h>
typedef void (*F1)(int, int);
typedef void (*F2)(int);
typedef void (*F3)(char, char, char, char);
void ff1(int a, int b) {
printf("ff1\r\n");
printf("%d,%d\r\n", a, b);
}
void ff2(int a) {
printf("ff2\r\n");
printf("%d\r\n", a);
}
void ff3(char a, char b, char c, char d) {
printf("ff3\r\n");
printf("%d,%d,%d,%d\r\n", a,b,c,d);
}
int main(int argc, char *argv[])
{
F1 f1 = (F1)ff2;
f1(1,2);
F2 f2 = (F2)ff1;
f2(3);
F2 f3 = (F2)ff3;
f3(4);
F3 f4 = (F3)ff1;
f4(5,6,7,8);
return 0;
}
运行结果:
可以看看ff3函数和最后一个ff1函数的运行结果,发现其实参数每次传递的最小单元为4字节。
同样,这里是不是也解释了一个非常基础的问题,有如下形式的调用:
void func(int a) {
a += 1;
}
int a = 1;
func(a);
最后a的值还是为1的原因。
因为在参数传递的时候,实际上是把参数的值复制到了栈空间的某段内存中,而函数在调用时,固定在该内存中读取需要的数据。
本人才疏学浅,如果有哪里不对的,请大家不吝赐教
留个邮箱吧:2999049@qq.com