最近我在一个LCD上想实现打印格式化字符串的功能,实现这样的功能可有两种方式:

一 最直接的就是自己去解析类似于printf功能的一个函数;

二 比较简单的方法是使用已有的sprintf功能,把格式化字符串打印到一个字符缓冲区中,再将这个字符缓冲区打印到LCD上来。

这里选择了第二种方法!

我已经把这个嵌入式程序弄到pc机上来运行了,第一次编写的代码是这样子的:

 #include <stdio.h>
#include <stdarg.h> void lcm_appendf(const char *fmt, ...)
{
char fm_buffer[];
int fm_len = ;
fm_len = snprintf(fm_buffer, , fmt); printf("fm_len = %d\r\n", fm_len);
printf("%s", fm_buffer);
} int main(int argc, char **args)
{
char c; // printf("Hello, this is Merlin Dec:%d, Hex:0x%x, Str:%s\r\n", 254, 0x32, "tfAna");
lcm_appendf("Hello, this is Merlin Dec:%d, Hex:%x, Str:%s\r\n", , 0x32, "tfAna");
scanf("%c", &c);
}

编译时提示错误:

$ gcc lcm_appendf.c
lcm_appendf.c: In function ‘lcm_appendf’:
lcm_appendf.c::: warning: format not a string literal and no format arguments [-Wformat-security]
lcm_appendf.c::: warning: format not a string literal and no format arguments [-Wformat-security]

表示没有格式参数!原来我忘记把格式(%d%x%s)对应的参数(254 0x32 "tfAna")写上去。所以先加上,这些可变参数,就是lcm_appendf的“第二个参数”...

直接写成snprintf(fm_buffer, , fmt, ...)显然是不行的!那么我怎么把lcm_appendf中的...转化为snprintf可以接受的参数呢?答案是使用va_list这个可变参数类型。

所以修改后的代码变成这样子:

 #include <stdio.h>
#include <stdarg.h> void lcm_appendf(const char *fmt, ...)
{
char fm_buffer[];
int fm_len = ;
va_list ap; va_start(ap, fmt);
fm_len = snprintf(fm_buffer, , fmt, ap);
va_end(ap); printf("fm_len = %d\r\n", fm_len);
printf("%s", fm_buffer);
} int main(int argc, char **args)
{
char c; // printf("Hello, this is Merlin Dec:%d, Hex:0x%x, Str:%s\r\n", 254, 0x32, "tfAna");
lcm_appendf("Hello, this is Merlin Dec:%d, Hex:%x, Str:%s\r\n", , 0x32, "tfAna");
scanf("%c", &c);
}

这时再编译就不再有什么错误提示了,但运行之后的结果是这样子的:

乱码!

网上百度了一圈没有找到答案,然后google了一下国外网站,看到stackoverflow上有人也有同样的问题

http://stackoverflow.com/questions/5977326/call-printf-using-va-list

原来,得使用vprintf啊!对应于我这里就是使用vsnprintf了。(参考这个函数的用法http://en.cppreference.com/w/cpp/io/c/vfprintf

再修改源码为:

 #include <stdio.h>
#include <stdarg.h> void lcm_appendf(const char *fmt, ...)
{
char fm_buffer[];
int fm_len = ;
va_list ap; va_start(ap, fmt);
fm_len = vsnprintf(fm_buffer, , fmt, ap);
va_end(ap); printf("fm_len = %d\r\n", fm_len);
printf("%s", fm_buffer);
} int main(int argc, char **args)
{
char c; // printf("Hello, this is Merlin Dec:%d, Hex:0x%x, Str:%s\r\n", 254, 0x32, "tfAna");
lcm_appendf("Hello, this is Merlin Dec:%d, Hex:%x, Str:%s\r\n", , 0x32, "tfAna");
scanf("%c", &c);
}

结果就正确了:

如何把va_list可变参数传送到下一级函数中(如传送到printf)的更多相关文章

  1. 【转载】va_list 可变参数 简介 va_copy vprintf

    [说明]本文转载自 smart 的文章 http://blog.sina.com.cn/s/blog_590be5290100qhxr.html  及百度百科 va_list是一个宏,由va_star ...

  2. va_list可变参数

    可变参数函数实现 va_list,va_start,va_arg,va_end va可变参数意思,variable-argument. 1. 头文件及实现 linux中定义在gcc头文件中,stdar ...

  3. js求和运算在可变参数的情况下ES3、ES5和ES6的写法区别

    //ES3.ES5的写法 function foo(){ var arr = Array.prototype.slice.call(arguments); var sum = 0; arr.forEa ...

  4. C++ 11可变参数接口设计在模板编程中应用的一点点总结

    概述 本人对模板编程的应用并非很深,若要用一句话总结我个人对模板编程的理解,我想说的是:模板编程是对类定义的弱化. 如何理解“类定义的弱化”? 一个完整的类有如下几部分组成: 类的名称: 类的成员变量 ...

  5. volatile,可变参数,memset,内联函数,宽字符窄字符,国际化,条件编译,预处理命令,define中##和#的区别,文件缓冲,位域

    1.volatile: 要求参数修改每次都从内存中的读取.这种情况要比普通运行的变量需要的时间长. 当设置了成按照C99标准运行之后,使用volatile变量之后的程序运行的时间将比register的 ...

  6. String filePath = request.getSession().getServletContext().getRealPath("/");这句话返回的路径是什么,解释下getRealPath("/")函数中的"/"表示什么意思

    request.getSession().getServletContext() 获取的是Servlet容器对象,相当于tomcat容器了.getRealPath("/") 获取实 ...

  7. MySQL下concat函数中null值问题

    在mysql中,使用CONCAT(str1,str2,...)函数拼接字符串的过程中,如果你拼接的字段当中有值为null,那么拼接的结果就为null 注: select CONCAT(字段1,字段2) ...

  8. 深入C语言可变参数(va_arg,va_list,va_start,va_end,_INTSIZEOF)

    一.什么是可变参数 在C语言编程中有时会遇到一些参数个数可变的函数,例如printf(),scanf()函数,其函数原型为: int printf(const char* format,…),int ...

  9. C语言可变参数va_list

    一.什么是可变参数 在C语言编程中有时会遇到一些参数个数可变的函数,例如printf(),scanf()函数,其函数原型为: int printf(const char* format,-) int ...

随机推荐

  1. iOS 执行ScrollView在空白空间-解

    iOS7 中间UIViewControllers配置automaticallyAdjustsScrollViewInsets.假设YES.scrollview它会根据status bar, navig ...

  2. ASP.NET Identity

    使用ASP.NET Identity实现基于声明的授权 阅读目录 走进声明的世界 创建并使用声明 基于声明的授权 使用第三方来身份验证 小节 在这篇文章中,我将继续ASP.NET Identity 之 ...

  3. WPF:警惕TextBox会占用过多内存

    原文:WPF:警惕TextBox会占用过多内存 问题源自这篇文章:WPF的TextBox产生内存泄露的情况. 整个问题是这样的,文章作者演示使用类似下方的代码来不停地像WPF的TextBox控件赋值: ...

  4. 三款经常使用IP发包工具介绍

    AntPower 版权全部© 2003 技术文章http://www.antpower.org 第1 页共14 页AntPower-技术文章三款经常使用IP 发包工具介绍小蚁雄心成员郎国军著lgj@q ...

  5. Notification使用以及PendingIntent.getActivity() (转)

    public void sendNotification(Context ctx,String message) { //get the notification manager String ns ...

  6. CodeIgniter连接数据库及快速入门

    原文:CodeIgniter连接数据库及快速入门 一.数据库配置 CodeIgniter 有一个配置文件让你存放数据库连接值(username:用户名,password:密码,database nam ...

  7. 【C++】智能指针auto_ptr简单的实现

    //[C++]智能指针auto_ptr简单的实现 #include <iostream> using namespace std; template <class _Ty> c ...

  8. EF调用sp,EF自动生成返回类型

    在sp中添加下面的红色部分,就是执行sp时的返回类型,后面在EF中添加该sp后,EF会在DBContext文件中,自动生成调用该sp的代码,包括返回类型等,如下: public virtual Obj ...

  9. C++11实现模板手柄:委托构造函数、defaultkeyword分析

    C++11.使用委托构造函数.和高速变量初始化,defaultkeyword重新声明默认构造函数,回答pod状态. 分析与推荐的方法. 到目前为止,VS2012和2013异常声明兼容还是停留在通信代码 ...

  10. Linq的Distinct太不给力了[转]

    假设我们有一个类:Product public class Product { public string Id { get; set; } public string Name { get; set ...