今天本来想定位下项目中日志打印部分的错误,尝试在变参的个数匹配不上时,编译给出warning或error,可惜弄了半天也没实现。
个人测试实现的变参函数代码如下,虽是测试代码,但也基本写明了变参的实现过程,需要注意的问题和相关要点都写在注释中了,下面上代码。
如果有哪位大侠能够给出我上面提到的warning和error的生成建议,欢迎留言或给我私信交流。废话少说,上代码了,可以直接g++ variable_arg.cpp编译成功。
/*
* File: variable_arg.cpp
* Author: Carl
*
* Created on August 28, 2012, 5:25 PM
*/
#include <cstdlib>
#include <cstdio>
#include <stdarg.h>
#include <cstring>
using namespace std;
/*
* 1)C标准规定实现可变参数机制的函数至少要有一个固定参数。
* 2)隐式类型不可用。例如,va_arg指定了类型是double,若传入的是int类型的变量10,就会报错
* 3)C语言的整型提升原则。也就是说,在va_arg中获取float和double使用的type是double,而获取char,short和int使用的type是int
* 4)函数指针不可用,除非用typedef定义过
*/
void miniprintf(const char *fmt, ...)
{
va_list ap; //声明一个对象,用来盛放参数,一个char链表(实际应该是一个连续的内存块,像数组一样),表现为一个指向char类型的指针
int d;
char c, *p, *s;
double f;
//初始化va_list,通过最后的固定参数实现对可变参数初始位置的定位,并未va_list分配内存,将可变参数复制到该内存块中,是va_list指向给内存块的初始位置
va_start(ap, fmt); //ap指向第一个(不是第0个)参数,fmt后面的那个
while (*fmt)
{
if (*fmt != '%') //普通字符直接输出
{
putchar(*fmt++);
continue;
}
fmt++; //格式化字符,++指向s,d之类的格式字符位
switch (*fmt++) //此处的++,目的是指向格式字符位的下一位,为下次loop准备
{
//根据相应格式来取到对应的匹配参数
case 's':
//va_arg,通过移动指针va_list获取由参数type(第二个参数)指定的变量,并返回该变量
s = va_arg(ap, char *);
printf("%s", s);
break;
case 'd':
d = va_arg(ap, int);
printf("%d", d);
break;
case 'c':
c = (char) va_arg(ap, int);
printf("%c", c);
break;
case 'f':
f = va_arg(ap, double);
printf("%f", f);
break;
default: //不被支持的格式字符
printf("the format [%c] is not supported", *fmt);
break;
}
}
va_end(ap); //这个最好都要调用一次,在有的C实现中,可能用来释放va_start为va_list分配的内存
return;
}
double sum(int lim, ...)
{
va_list ap;
double total = 0;
int i;
va_start(ap, lim);
for (i = 0; i < lim; i++)
{
total += va_arg(ap, double); //依次取到每个参数
}
va_end(ap);
return total;
}
int main(int argc, char** argv)
{
char str[10];
memset(str, 0, sizeof (str));
memcpy(str, "hello", 5);
miniprintf("this is the string \"%s\", \nthis is the int %d, \nthis is the character %c\n", str, 3, 'a');
double total = 0;
total = sum(5, 10.0, 20.0, 30.0, 40.0, 50.0); //这里如果是10,20这种int型的数据,会得到错误的结果
miniprintf("total is %f\n", total);
return 0;
}
运行结果如下
this is the string "hello",
this is the int 3,
this is the character a
total is 150.000000
水平有限,如果有朋友发现错误,欢迎留言交流。
转载请保留本文链接,如果觉得我的文章能帮到您,请顶一下。,谢谢。
分享到:
相关推荐
C语言变参_实现自己的printf,详细讲解变参在堆栈中的存放和读取,讲解如何实现一个变参函数,最终给出实现一个printf的参考,对于不能支持printf的系统非常有帮助,建议使用标准头文件stdarg.h。
如果读者对C语言可变参数函了解不多,可参考拙文「亲密接触C可变参数函数」,本文不再讲述C语言可变参数函数本身的定义,以及va_start、va_arg和va_e
变参函数用途很多,其通过设计,对外提供变参接口,允许上层业务层自由地通过格式化字符串来实现对自己输出行为的控制,这在很多debug和syslog日志输出场合很有用,我的书《0bug-C/C++商用工程之道》里面,第五章...
在学习C语言的过程中我们可能很少会去写变参函数,印象中大学老师好像也没有提及过,但我发现变参函数的实现很巧妙,所以还是特地在此分析下变参函数的实现原理。无需标准C的支持,我们自己写代码来实现。
c语言实现的一个简单的日志函数, 主要是实现可变参数函数的使用技巧!
本篇文章是对用c语言根据可变参数合成字符串的方法进行了详细的分析介绍,需要的朋友参考下
自己写的变参函数,printf函数的实现(可输出整数,字符,字符串)
传统链表的实现方法和注意事项,以及对传统链表实现方法的颠覆; 与函数参数、变参函数、函数调用、函数指针相关的一些难理解和容易被理解错的知识点解析; 文件和指针的使用原则、技巧和注意事项; ...
传统链表的实现方法和注意事项,以及对传统链表实现方法的颠覆; 与函数参数、变参函数、函数调用、函数指针相关的一些难理解和容易被理解错的知识点解析; 文件和指针的使用原则、技巧和注意事项; ...
第一,去掉了原来使用的goto语句,因为C语言中除了错误处理之外,不建议使用goto语句; 第二,fmt和pnt的含义更加明确,它们始终指向下一个需要处理的字符和变参;
2.4 在C语言中用什么方法实现抽象数据类型最好? 22 *2.5 在C语言中是否有模拟继承等面向对象程序设计特性的好方法? 22 2.6 为什么声明extern f(struct x *p); 给我报了一个晦涩难懂的警告信息? 23 2.7 我...
2.4 在C语言中用什么方法实现抽象数据类型最好? *2.5 在C语言中是否有模拟继承等面向对象程序设计特性的好方法? 2.6 为什么声明externf(structx*p);给我报了一个晦涩难懂的警告信息? 2.7 我遇到这样声明...
难道在C语言中一个结构不能包含指向自己的指针吗? . . . . 3 1.7 怎样建立和理解非常复杂的声明?例如定义一个包含N 个指向返 回指向字符的指针的函数的指针的数组? . . . . . . . . . . . . . . 3 1.8 函数只定义...
难道在C语言中一个结构不能包含指向自己的指针吗? o 2.7 怎样建立和理解非常复杂的声明?例如定义一个包含 N 个指向返回指向字符的指针的函数的指针的数组? o 2.8 函数只定义了一次, 调用了一次, 但编译器提示...
在日常的嵌入式开发过程中,常常会...本文运用变参函数的知识,提供一种实现printf的格式化输出的实现代码供大家参考。 教程阅读见:https://blog.csdn.net/qq_44078824/article/details/118440458 使用vs环境编写。
sprintf 是个变参函数,定义如下: int sprintf( char *buffer, const char *format [, argument] … ); 除了前两个参数类型固定外,后面可以接任意多个参数。printf 和sprintf 都使用格式化字符串来指定串的格式,...