当前位置: 首页 > news >正文

C++ printf族函数

1、所有的printf族函数

有两大类:

#include<stdio.h>
int printf(const char* format, ...);
int fprintf(FILE* stream, const char* format, ...);
int dprintf(int fd, const char* format, ...);
int sprintf(char* str, const char* format, ...);
int snprintf(char* str, size_t size, const char* format, ...);
#include<stdarg.h>
int vprintf(const char* format, va_list ap);
int vfprintf(FILE* stream, const char* format, va_list ap);
int vdprintf(int fd, const char* format, va_list ap);
int vsprintf(char* str, const char* format, va_list ap);
int vsnprintf(char* str, size_t size, const char* format, va_list ap);

从函数名来看,下面的函数的名字都比上面多一个字母 v
从参数类型来看,下面函数的最后一个参数都是va_list类型。
下面的函数在功能上与上面的函数一 一对应。

2、前驱知识

本小节从从最基础的 printf 入手,来讲解学习这些函数需要的前驱知识。

2.1 什么是: 格式化字符串format

这个其实我们每天都在用,只是不知道他叫这个名字。

int a = 1;
double b = 3.14;
printf("a = %d, b = %llf\n", a, b);

结合上面printf函数的定义可知, "a = %d, b = %llf\n" 其实就是格式化字符串,即,参数format的内容。而ab合起来是可变参数参数,即,参数...的内容。

2.2 va_list是什么类型

va_list 是可变参数列表类型,它的含义和...一样,都是代表很多个参数。但它们的区别也很大。

准确来说 ...并不能算作一种类型,他只是在视觉上告诉我们这后面可以传入可变参数。而在函数的实现中我们并不能直接使用...作为参数名来使用这些可变参数。那,我们想自己实现一个形如 my_printf(const char* format, ...) 的函数该怎么办呢??

va_list 类型就是用来解决这个问题的。在函数体中,我们使用va_list类型的一个参数就可以代表...中所有的可变参数。

好嘞,本小节标题的问题解决了。

3、可变参数列表(va_list)怎么用?

3.1 va_list族函数

#include<stdarg.h>
void va_start(va_list ap, last);	
type va_arg(va_list ap, type);
coid va_end(va_list ap);
void va_copy(va_list dest, va_list src);

3.2 这些函数怎么用

直接以一个实际应用中的例子来说明。现在,我们的程序中需要一个这样的函数:func(int a, double b, ...);

首先,在函数体中定义一个va_list类型的变量:va_list arg_list;

此时arg_list还未初始化,类似一个空指针,接下来要把这个空指针指定到需要的位置。va_start函数就用来完成这件事,使用va_list类型时,该函数必须第一个被调用:
va_start(arg_list, b);
调用该函数后,arg_list就能代表传入func所有可变参数。va_start函数中,参数last没有类型,就是一个参数名,他是fun中最后一个类型已知的参数的名字,本例中就是b

然后,arg_list就能正式投入使用了。我们既可以将他看作一个整体,传入printf函数的第二个参数,也可以把可变参数一个一个取出来用。
先来看前者:直接这样:printf(" 这里是格式化字符串 ", arg_list);
后者要复杂一些,需要使用va_arg函数。进行 va_arg(arg_list, int) 调用就是从开始位置返回一个int(可能不是int类型,根据实际情况确定类型),并把位置往前推1,下次调用该函数就返回后面的那个参数。这样访问需要提前知道可变参数的数量,类型以及顺序。

va_arg函数本身并不知道在哪里结束,所以用while(1){ var_arg(args,int);} 会无限循环直到内存访问越界程序coredump。 最好就在传参时传入一个专门用于标识结尾的参数 比如12345678 。当返回值等于标识数时,结束访问。

访问完毕后,必须使用va_end函数销毁刚刚初始化的arg_listva_end(arg_list);

3.3 总结

va_start:初始化(指定位置)
va_arg:遍历访问
va_end:销毁
va_copy:复制

4、再看printf族函数

#include<stdio.h>
int printf(const char* format, ...);				将输出写入标准输出 stdout
int fprintf(FILE* stream, const char* format, ...);	将输出写入给定的输出流
int dprintf(int fd, const char* format, ...);		将输出写入文件描述符
int sprintf(char* str, const char* format, ...);	将输出写入字符串
int snprintf(char* str, size_t size, const char* format, ...);	将定长输出写入字符串

使用va_list类型的函数功能与上面的一 一对应,只是在调用时,把上面的...变量用va_list变量替代。

#include<stdarg.h>
int vprintf(const char* format, va_list ap);
int vfprintf(FILE* stream, const char* format, va_list ap);
int vdprintf(int fd, const char* format, va_list ap);
int vsprintf(char* str, const char* format, va_list ap);
int vsnprintf(char* str, size_t size, const char* format, va_list ap);

5、asprintfvasprintf函数

查看man手册发现,这两个函数并没有和开头列出的10个函数放在一起,其定义如下:

#define _GNU_SOURCE
#include<stdio.h>

int asprintf(char** strp, const char* fmt, ...);
int vasprintf(char** strp, const cahr* fmt, va_list ap);

这两个函数的功能其实和sprintfvsprintf一样,只是上面的两个函数分配了一个足够大的字符串来保存输出,包括终止字符'\0',并通过第一个参数返回指向它的指针。最后该指针应该传递给free,以释放不再需要的存储空间。

使用实例代码:

void LogEvent::format(const char* fmt, va_list al)
{
	char* buf;
	int len = vasprintf(&buf, fmt, al);
	if (len != -1)
	{
		m_ss << std::string(buf, len);		m_ss 是一个 stringstream 类型的字符流对象
		free(buf);
	}
}

相关文章:

  • 最小生成树的常用算法模板
  • 一个服务器压力测试程序
  • 图论——二分图
  • 面向对象程序设计———组合、委托 与 继承
  • C++设计模式
  • C++ 嵌套类
  • CMake指令解析 set(CMAKE_CXX_FLAGS “$ENV{CXXFLAGS} -rdynamic -O3 -fPIC -ggdb -std=c++11 -Wall -Wno-deprec
  • 记一个测试sylar服务器日志模块时遇到的一个非常奇怪的问题
  • syscall()
  • 记一个编写宏时的错误
  • C++中全局变量,静态变量,静态局部变量 的初始化和内存分配问题
  • C++ 模板实现单例模式
  • 《C++ Primer》 异常
  • C++父类和子类指针的相互赋值和转换
  • 算法设计与分析————期末死亡冲刺
  • [nginx文档翻译系列] 控制nginx
  • 【译】理解JavaScript:new 关键字
  • Essential Studio for ASP.NET Web Forms 2017 v2,新增自定义树形网格工具栏
  • Eureka 2.0 开源流产,真的对你影响很大吗?
  • HTTP传输编码增加了传输量,只为解决这一个问题 | 实用 HTTP
  • java B2B2C 源码多租户电子商城系统-Kafka基本使用介绍
  • JavaScript创建对象的四种方式
  • Linux快速配置 VIM 实现语法高亮 补全 缩进等功能
  • October CMS - 快速入门 9 Images And Galleries
  • oldjun 检测网站的经验
  • session共享问题解决方案
  • supervisor 永不挂掉的进程 安装以及使用
  • 从零开始学习部署
  • 极限编程 (Extreme Programming) - 发布计划 (Release Planning)
  • 计算机在识别图像时“看到”了什么?
  • 前端 CSS : 5# 纯 CSS 实现24小时超市
  • 全栈开发——Linux
  • 山寨一个 Promise
  • 使用 QuickBI 搭建酷炫可视化分析
  • 算法---两个栈实现一个队列
  • ​ssh-keyscan命令--Linux命令应用大词典729个命令解读
  • #1015 : KMP算法
  • #NOIP 2014# day.1 T2 联合权值
  • #QT(智能家居界面-界面切换)
  • (第二周)效能测试
  • (动态规划)5. 最长回文子串 java解决
  • (译) 函数式 JS #1:简介
  • (转)chrome浏览器收藏夹(书签)的导出与导入
  • (转)linux下的时间函数使用
  • (转)创业的注意事项
  • (转)详解PHP处理密码的几种方式
  • (轉)JSON.stringify 语法实例讲解
  • *(长期更新)软考网络工程师学习笔记——Section 22 无线局域网
  • ***详解账号泄露:全球约1亿用户已泄露
  • .net开源工作流引擎ccflow表单数据返回值Pop分组模式和表格模式对比
  • .NET使用存储过程实现对数据库的增删改查
  • .Net中wcf服务生成及调用
  • .pub是什么文件_Rust 模块和文件 - 「译」
  • @我的前任是个极品 微博分析
  • [20190416]完善shared latch测试脚本2.txt