Arce 发表于 2021-8-6 13:58:22

全新版本printf函数你可能没玩过

C语言可变参数函数C 语言允许定义参数数量可变的函数,这称为可变参数函数。这种函数需要固定数量的强制参数,后面是数量可变的可选参数。这种函数必须至少有一个强制参数。可选参数的类型可以变化。可选参数的数量由强制参数的值决定,或由用来定义可选参数列表的特殊值决定。
C 语言中最常用的可变参数函数例子是 printf()和 scanf()。这两个函数都有一个强制参数,即格式化字符串。格式化字符串中的转换修饰符决定了可选参数的数量和类型。对于每一个强制参数来说,函数头部都会显示一个适当的参数,像普通函数声明一样。参数列表的格式是强制性参数在前,后面跟着一个逗号和省略号(...),这个省略号代表可选参数。
可变参数函数要获取可选参数时,必须通过一个类型为 va_list 的对象,它包含了参数信息。这种类型的对象也称为参数指针(argument pointer),它包含了栈中至少一个参数的位置。可以使用这个参数指针从一个可选参数移动到下一个可选参数,由此,函数就可以获取所有的可选参数。va_list 类型被定义在头文件 stdarg.h 中。

如何实现C语言可变参数通过三个宏(va_start、va_end、va_arg)和一个类型(va_list)实现的

[*]void va_start ( va_list ap, paramN );功能:初始化可变参数列表
[*]void va_end ( va_list ap );功能:关闭初始化列表(将 ap 置空)。
[*]type va_arg ( va_list ap, type );功能:返回下一个参数的值。
好了,综合上面3个宏和一个类型可以猜出如何实现C语言可变长参数函数:
va_start 获取参数列表(的地址)存储到 ap 中,va_arg 逐个获取值,最后用 va_arg 将 ap 置空。

小试牛刀#include <stdio.h>#include <stdarg.h>#define END -1int va_sum(int first_num, ...){    // (1) 定义参数列表    va_list ap;    // (2) 初始化参数列表    va_start(ap, first_num);    int result = first_num;    int temp = 0;    // 获取参数值    while ((temp = va_arg(ap, int))!=END){      result += temp;    }    // 关闭参数列表    va_end(ap);    return result;}int main(){    printf("%d\n", va_sum(1, 2, 3, 4,END));    printf("%d\n", va_sum(1, 2, 3, 4, 5, END));    return 0;}注意项
[*]宏定义在 stdarg.h 中,所以使用时,不要忘了添加头文件。
[*]设定一个参数结束标志(cplusplus 上说,va_arg 并不能确定哪个参数是最后一个参数)。
[*]类型的匹配
渐入佳境我们一起来偷看下“内裤”,发现标准库中是这样声明printf函数的。

最终你要学习就是这个:int printf(const char * format, ...);下面我们来写一个简单的可变参数的C 函数printf函数
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
int myvfprintf(FILE* stream, const char* format, va_list arglist)
{
int translating = 0;
int ret = 0; //记录最终输出的字符个数
const char* p = 0;
for (p = format; *p != '\0'; ++p)
    {
switch (*p)
      {
case '%':
if (!translating)
            {
                translating = 1; //translating置为1,代表后面的字符需要解析
            }
else
            {
if (fputc('%', stream) < 0)
return EOF;
                ++ret;
                translating = 0;
            }
break;
case 'd':
if (translating) //%d
            {
char buf;
                translating = 0;
                _itoa_s(va_arg(arglist, int), buf, 10);
if (fputs(buf, stream) < 0)
return EOF;
                ret += strlen(buf);
            }
else if (fputc('d', stream) < 0)
return EOF;
else
                ++ret;
break;
case 's':
if (translating)
            {
const char* str = va_arg(arglist, const char*);
                translating = 0;
if (fputs(str, stream) < 0)
return EOF;
                ret += strlen(str);
            }
else if (fputc('s', stream) < 0)
return EOF;
else
                ++ret;
break;
default:
if (translating)
                translating = 0;
if (fputc(*p, stream) < 0)
return EOF;
else
                ++ret;
break;

      }
    }
return ret;
}
int myprintf(const char* format, ...)
{
    va_list(arglist);
    va_start(arglist, format);
return myvfprintf(stdout, format, arglist);
}
int main(){
printf("%s\n", "ILoveyou");
    myprintf("%s\n", "ILoveyou");
return 0;
}运行测试结果:


结束语文章都是手打原创,每天最浅显的介绍C语言、C++,windows知识,喜欢我的文章就关注一波吧,每天带你学习C/C++不同的知识,也可以看到最新更新和之前发表的文章哦。如果今天学到知识的,可以在留言区留言学到了哦,如果喜欢可以收藏,转发,评论哦,这真的对我很重要!!


文档来源:51CTO技术博客https://blog.51cto.com/u_15297386/3289787
页: [1]
查看完整版本: 全新版本printf函数你可能没玩过