Bootstrap

C语言不定参数函数


众所周知,C语言不支持函数重载,支持函数重载的是C++。究其原因,C语言在编译函数之后,将"_函数名"存放到函数库,而C++在编译函数之后,将"_函数名_参数类型_参数类型"存放到函数库

但是,在查询fcntl函数时,却发现其函数原型有多个

int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd, struct flock *lock);

难道C语言也支持函数重载?于是我深入了解了相关知识,并将其整理分享

首先,C语言确实不支持函数重载,这种现象被称为"不定参数函数"

不定参数

我们都经常接触不定参数,因为printf就是最常见的不定参数函数,它的函数原型如下:

int printf( const char *format, ... );              //c99前
int printf( const char *restrict format, ... );     //c99起

如上所示,"…"表示不定参数,它必须写在函数列表的最后

不定参数实现原理

C语言引入了三个宏来处理不定参数的问题,如下所示:

//va_arg()、va_start()、va_end()
#define _INTSIZEOF(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1)) //类型n的大小,这是考虑了字节对齐
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) ) //ap指向第一个不定参数地址
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) ) //下一个参数地址  返回当前ap指向的值,并且增加ap
#define va_end(ap) ( ap = (va_list)0 ) // 将指针置为无效

type va_arg(va_list argptr, type);  
void va_end(va_list argptr);  
void va_start(va_list argptr, last_parm);  

通过上述宏的时候,可以将函数传入堆栈的参数通过指针读取出来(通过va_start和va_arg传入的参数类型作为指针的偏移量,即可将堆栈中存放的数据取出)

示例代码

#include <stdio.h>
#include <stdarg.h>

int sum(int a, ...)
{
    va_list var_ptr;
    va_start(var_ptr, a);

    int sum = a;

    a = va_arg(var_ptr, int);

    while(a != 0)
    {
            sum += a;
            a = va_arg(var_ptr, int);		//获取下一个参数值
    }
    va_end(var_ptr);
    return sum;
}

int main()
{
    int i = sum(1,2,3,4,5,6,7,8,9,0);
    printf("The sum is: %d\n",i);
    return 0;
}

程序运行结果:

The sum is: 45
;