关于函数指针类型强制转换的一些摸索

今天在研究一位牛人写的代码,发现了一个以前没有注意过的东西,函数指针的类型强制转换。


指针的类型强转在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






  • 20
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值