C语言中变参函数传参的实现示例

 更新时间:2021年08月12日 10:20:08   作者:lularible  
本文主要介绍了C语言中变参函数传参,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

背景引入

近期在看一本书,叫做《嵌入式C语言自我修养》,写的内容对我帮助很大,是一本好书。在第6章,GNU C编译器扩展语法精讲一节,这本书给出了一些变参函数的例子:

//1.变参函数初体验
#include<stdio.h>
void print_num(int count,...)
{
	int *args;
	args = &count + 1;
	for(int i = 0;i < count;i++)
	{
		printf("*args:%d\n",*args);
		args++;
	}
}

int main(void)
{
	print_num(5,1,2,3,4,5);
	return 0;
}

上面的代码很好理解:定义一个变参函数print_num,在函数内部先取得第一个参数的地址赋值给一指针,然后将指针后移,取得后面的参数并打印出来。在main函数中,传给print_num 6个参数,按这个逻辑,应该是打印出:

*args:1
*args:2
*args:3
*args:4
*args:5

但是结果却出人意料:

打印出的值和传进去的值完全不相等,甚至毫无规律可言。

问题分析

上述代码中,是通过取首个参数的地址,并往后移动这个指针来获得后面参数的,那么问题很可能出在两个地方:

  • 指针移动的方式不正确
  • 参数的地址排布可能不是连续的

我们一个一个来看,先暂且假定这些参数地址是连续的,且相隔一样的距离。那么我们就可以聚焦于指针的移动方式了。指针移动是“args++”这一行语句来控制的。笔者修改了一下书上的代码:

#include<stdio.h>
void print_num(int count,...)
{
	int *args;
	args = &count;
	for(int i = 0;i <= count;i++)
	{
		printf("addr:%p\n",args);
		printf("*args:%d\n",*args);
		args++;
	}
}

int main(void)
{
	print_num(5,1,2,3,4,5);
	return 0;
}

主要增加了对于每个参数的地址的打印,运行结果如下:

笔者发现这个"args++"每次往后移动4个字节,这是因为对于"int"型指针的移动操作,是以4(sizeof(int))为基本单位的。同理,对于"char"型指针的移动操作,以1(sizeof(char))为单位。

指针大小

一个"int"型指针大小如果等于4,那么上述对于指针移动操作就没问题。可是"int"型指针大小真的等于4吗?

笔者用代码来测试下:

#include<stdio.h>

int main()
{
	char*	charPoint;
	int*	intPoint;
	double*	doublePoint;

	struct st{
		int first;
	};

	struct st *structPoint;

	printf("sizeof(char*):%ld\n",sizeof(charPoint));
	printf("sizeof(int*):%ld\n",sizeof(intPoint));
	printf("sizeof(double*):%ld\n",sizeof(doublePoint);
	printf("sizeof(struct*):%ld\n",sizeof(structPoint));
	return 0;
}

运行结果:

可以看到,不仅"int"型指针是8字节大小,"char"、"double"和结构体指针也都是8字节大小。这是因为笔者电脑安装的是64位系统。所以书上代码的"int"型指针自增操作不适用于笔者,笔者将其改为“args += 2”,在dev c++这个IDE中可以得到正确的结果,但在ubuntu gcc下还是不对。

参数位置排布

解决了第一个指针移动步长问题,还是得不到正确答案。笔者怀疑参数地址很可能不连续。如何看函数的参数地址信息?方法有很多,笔者就选一种比较快捷的方式——看汇编代码。

在ubuntu的终端框输入

gcc -S [源文件]

就能得到一个带".s"后缀的汇编代码文件。

我们对比着看main函数与print_num函数中关于参数传递的部分:

在main函数中,各个参数被放入不同的寄存器,在print_num函数中,又从寄存器中将参数取出来放入print_num的函数堆栈中。仔细看各个参数最终被放入的堆栈位置,发现第一个参数地址和第二个参数地址差了28个字节,而后面的参数地址之间都是差8个字节。这也就解释了为何之前的代码结果不对了。

解决问题

所以只要在第一个参数地址的基础上加上偏移量28即可("char*"型)。

运行结果符合预期:

但是为什么第一个参数和第二个参数间隔28字节,笔者暂时还不清楚,盲猜需要去看gcc中编译器的相关知识。

额外的测试

以往对于固定参数个数的普通函数的传参,是这样处理的:前几个参数放入寄存器,若个数超出,则压入函数堆栈。笔者有点好奇变参函数是否也如此,就给这个print_num传了18个参数:

汇编代码如下:

这说明了变参函数的传参规则和普通函数并无两样。

总结

在看书的时候,我喜欢边看边敲代码,这一次照着书上敲的代码运行结果不对,就有了上面的一些探究过程。如果我没有动手实践,以后碰到类似问题时很可能会蒙圈。所以动手实践很有必要。

另外,书上的东西并不一定全对,并且它的正确性需要有特定的前提做保证。比如,要是我使用的是32位系统,且编译器在处理变参函数时将参数连续压栈,那么书上的代码就是完全正确的。我们无需害怕这些坑,我们需要做的就是去找到这些前提条件,去找到问题的本质点,最后解决问题。

参考资料

《嵌入式C语言自我修养——从芯片、编译器到操作系统》

到此这篇关于C语言中变参函数传参的实现示例的文章就介绍到这了,更多相关C语言变参函数传参内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 关于C++的.cpp文件运行全过程

    关于C++的.cpp文件运行全过程

    这篇文章主要介绍了C++的.cpp文件运行全过程,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-02-02
  • C/C++实现贪吃蛇逐步运动效果

    C/C++实现贪吃蛇逐步运动效果

    这篇文章主要为大家详细介绍了C/C++实现贪吃蛇逐步运动效果的相关资料,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-05-05
  • 一问了解C++ 的移动语义

    一问了解C++ 的移动语义

    本文主要介绍C++ 的移动语义,移动语义并不是一个容易理解的概念,很多程序员可能对其存在一定的疑惑,今天我们就来探讨一下 C++ 中的移动语义
    2023-04-04
  • C语言调用摄像头实现生成yuv未压缩图片

    C语言调用摄像头实现生成yuv未压缩图片

    这篇文章主要为大家详细介绍了C语言如何调用摄像头实现生成yuv未压缩图片,文中的示例代码讲解详细,具有一定的学习价值,感兴趣的小伙伴可以参考一下
    2023-11-11
  • C语言关键字const和指针的结合使用

    C语言关键字const和指针的结合使用

    这篇文章主要介绍了C语言关键字const和指针的结合,本文给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-02-02
  • C语言 详细讲解#pragma的使用方法

    C语言 详细讲解#pragma的使用方法

    #pragma 指令对每个编译器给出了一个方法,在保持与C和C++语言完全兼容的情况下,给出主机或操作系统专有的特征。依据定义,编译指示是机器或操作系统专有的, 且对于每个编译器都是不同的
    2022-04-04
  • c++中的string常用函数用法总结

    c++中的string常用函数用法总结

    以下是对c++中的string常用函数的用法进行了详细的分析介绍,需要的朋友可以过来参考下
    2013-09-09
  • 深入解析C++ STL中的常用容器

    深入解析C++ STL中的常用容器

    这里我们不涉及容器的基本操作之类,只是要讨论一下各个容器其各自的特点。STL中的常用容器包括:顺序性容器(vector、deque、list)、关联容器(map、set)、容器适配器(queue、stac)
    2013-09-09
  • Linux下Select多路复用实现简易聊天室示例

    Linux下Select多路复用实现简易聊天室示例

    大家好,本篇文章主要讲的是Linux下Select多路复用实现简易聊天室示例,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下,方便下次浏览
    2021-12-12
  • C++实现简单推箱子小游戏

    C++实现简单推箱子小游戏

    这篇文章主要为大家详细介绍了C++实现简单推箱子小游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-08-08

最新评论