如果一定要说哪个C语言代码最“出名”,我觉得是“hello world”。大多数初学者一生中写的第一个C语言代码是这个里程碑式的代码:
#包含stdio.h
int main()
{
printf(' hello world n ');
返回0;
}
因为这个著名的程序,printf()函数成为大多数C语言初学者接触到的第一个标准库函数。
“里程碑”代码
00-1010随着学习的推进,初学者逐渐学会调用其他C语言函数并定义自己的函数,观察力敏锐的人会注意到printf()函数似乎与其他函数不同。——printf()函数没有固定数量的参数,似乎可以接收任意数量的参数。
其他C语言函数不一样。它们中的大多数都有固定数量的参数(0、3等)。),并且必须传递相应数量的参数来调用这些函数。
一些持有“特殊理论”的初学者认为像printf()这样的“可变参数”函数是“特殊的”,是系统定义的。我们程序员只能定义固定参数函数,但他们不能。c语言有办法定义自己的“可变参数”函数。
printf()是一个只能由系统定义的“特殊”函数吗?
00-1010事实上,标准库stdarg.h便于C语言程序员定义自己的“变量参数”函数。如果读者像我一样使用Linux系统,他们可以通过man命令轻松查询相关库函数:
C语言标准库
头文件stdarg.h声明了va_list类型来描述变量参数,并定义了上述四种解析方法。我不打算在这里介绍太多枯燥的理论知识。让我们直接看例子。请参考相关的C语言代码:
c语言代码
上面的代码定义了可变参数函数foo(),它可以接收类似printf()的函数,并将fmt中的S解析为字符串,D解析为整数,C解析为字符。因此,编译并执行这段C语言代码,可以得到如下输出:
# gcc t.c
# ./a.out
字符串你好
int 12
char m
通过这个例子,我们可以看到用C语言定义变量参数函数并不复杂。在处理变量参数时,我们只需要调用va_start()将参数序列加载到va_list结构的变量中,然后依次调用va_arg()进行分析。解析后,调用va_end()结束解析。
va_start - va_arg - va_end .唯一需要注意的是,当使用va_arg()解析参数时,需要指定类型。不过,这个过程也很简单。可变参数函数的实现者可以指定一组规则来限制函数调用方传递的参数,从而知道下一步需要解析什么类型的参数。比如上面的C语言代码规定fmt中的S表示下一个要解析的参数是字符串,D表示整数等。
计算机如何处理可变参数函数?
用00-1010c语言定义变量参数函数的过程并不复杂。借助stdarg.h,我们可以轻松定义接收任意多参数的函数。然而到了现在,有读者发现了问题:我们人类可以根据规则编写变量参数函数,但是计算机是如何理解这组规则的呢?或者换句话说,计算机是如何处理“可变参数”的?
ify">以Linux为例,看过我之前文章的读者应该明白,每个C语言程序进程都有属于自己的栈,进程中的每个函数则有属于自己的栈帧,当有函数调用时,例如:foo("%d%d%d", 3,2,1);C语言编译器会产生类似于下面这样的汇编代码:
push 1 push 2 push 3 push "%d%d%d" call foo也即将 foo() 函数的参数先压入栈中,然后再调用 foo() 函数。鉴于栈这种数据结构“先进后出”的特点,一般函数参数的入栈顺序是从右至左的。
[] // 空栈 ------------------------------- push 1: [1] ------------------------------- push 2: [1] [2] ------------------------------- push 3: [1] [2] [3] // 参数 1 2 3 被压入栈中 ------------------------------- push "%d%d%d":[1] [2] [3] ["%d%d%d"] ------------------------------- call foo ... // foo 函数开始使用参数按照这样的参数入栈顺序,foo() 函数使用参数很方便,依次从栈中将参数取出就可以了。至于如何解析栈中的参数,则可以根据可变参数实现者指定的规则,例如在格式化字符串 fmt 中遇到 s 就解析为字符串等。
如果可变参数 foo() 接收到其他数目的参数,对于最终程序来说,也仅仅只需要修改压栈的参数数目,其他并无太多不同。
小结
本文主要讨论了C语言中可变参数函数的定义方法,以及计算机如何处理可变参数函数的过程,其实并不复杂。C语言不像C++那样支持函数重载,但是借助于可变参数函数和宏,我们可以像定义“伪类”那样,定义自己的“伪函数重载”,这是一种编程技巧,以后有机会再讨论了。
欢迎在评论区一起讨论,质疑。文章都是手打原创,每天最浅显的介绍C语言、linux等嵌入式开发,喜欢我的文章就关注一波吧,可以看到最新更新和之前的文章哦。
未经许可,禁止转载。