C、C++格式化字符串
引言
在C和C++开发中,我们经常会用到printf来进行字符串的格式化,例如printf("format string %d, %d", 1, 2);
,这样的格式化只是用于打印调试信息。printf函数实现的是接收可变参数,然后解析格式化的字符串,最后输出到控制台。那么问题来了,当我们需要实现一个函数,根据传入的可变参数来生成格式化的字符串,应该怎么办呢?
你可以在这里看到更好的排版
正文
可变参数
首先来一个可变参数使用示例,testVariadic
方法接收int行的可变参数,并以可变参数为-1表示结束。va_list用于遍历可变参数,va_start
方法接收两个参数,第一个为va_list
,第二个为可变参数前一个参数,下面的例子里该参数为a。
/**
下面是 <stdarg.h> 里面重要的几个宏定义如下:
typedef char* va_list;
void va_start ( va_list ap, prev_param ); // ANSI version
type va_arg ( va_list ap, type );
void va_end ( va_list ap );
va_list 是一个字符指针,可以理解为指向当前参数的一个指针,取参必须通过这个指针进行。
<Step 1> 在调用参数表之前,定义一个 va_list 类型的变量,(假设va_list 类型变量被定义为ap);
<Step 2> 然后应该对ap 进行初始化,让它指向可变参数表里面的第一个参数,这是通过 va_start 来实现的,第一个参数是 ap 本身,第二个参数是在变参表前面紧挨着的一个变量,即“...”之前的那个参数;
<Step 3> 然后是获取参数,调用va_arg,它的第一个参数是ap,第二个参数是要获取的参数的指定类型,然后返回这个指定类型的值,并且把 ap 的位置指向变参表的下一个变量位置;
<Step 4> 获取所有的参数之后,我们有必要将这个 ap 指针关掉,以免发生危险,方法是调用 va_end,他是输入的参数 ap 置为 NULL,应该养成获取完参数表之后关闭指针的习惯。说白了,就是让我们的程序具有健壮性。通常va_start和va_end是成对出现。
*/
//-1表示可变参数结束
void receiveVariadic(int a, ...) {
va_list list;
va_start(list, a);
int arg = a;
while (arg != -1) {
arg = va_arg(list, int);
printf("%d ", arg);
}
printf("\n");
va_end(list);
}
//test
void testVari()
{
printf("------%s------\n", __FUNCTION__);
//-1表示可变参数结束
receiveVariadic(1, 2, 3, 4, 5, 6, -1);
}
运行结果
------testVari------
2 3 4 5 6 -1
格式化字符串
好了,我们已经介绍了怎样实现一个接收可变参数的C函数,接下来介绍根据接收的可变参数来格式化字符串。这里介绍两种方式,第一种是利用宏定义,第二种通过函数的方式来实现。
通过宏定义的方式
en…让咱们先来看看第一个版本的宏,这个宏定义对于不熟悉宏的人来说可能看着有点费劲,不过不要怕,稍后会做解释,代码如下:
#define myFormatStringByMacro_WithoutReturn(format, ...) \
do { \
int size = snprintf(NULL, 0, format, ##__VA_ARGS__);\
size++; \
char *buf = (char *)malloc(size); \
snprintf(buf, size, format, ##__VA_ARGS__); \
printf("%s", buf); \
free(buf); \
} while(0)
宏基础知识
首先需要介绍宏用到的知识:\
, 这个\
的作用是可换行定义宏,毕竟如果一行很长的宏可读性很差,使用方式在换行时加上\
即可。第二个是介绍(format, ...)
,这里的...
是预定义的宏,用于接收可变参数,就像是printf
函数一样。接着介绍##__VA_ARGS__
,同样的__VA_ARGS__
也是预定义的宏,表示接收到的...
传入的可变参数。##
的作用是用来处理未传入可变参数的情况,当没有传入可变参数的时候,编译器或通过优化将snprintf(NULL, 0, format, ##__VA_ARGS__);
优化为snprintf(NULL, 0, format);
。你可以理解为没有可变参数时,##
前的逗号,
与__VA_ARGS__
都被“干掉了”。
你一定会觉得困惑,为什么要写do-while
语句呢?这是为了宏的健壮性,如果使用宏的人像下面这样使用的话,就会出问题
#define testMarco(a, b) \
int _a = a + 1; \
int _b = b + 1; \
printf("\n%d", _a + _b); \
void test()
{
if (1 > 0)
testMarco(1, 2);
}
上面的代码连编译都不会通过, 会报错如下:
如果手动展开这个宏的话,会变成这个样子,问题就显而易见了。但是如果if
语句加上了{}
的话,就不会有问题,可以看出规范写法是多么的重要
C、C++格式化字符串的更多相关文章
- VBA 格式化字符串 - Format大全
VBA 格式化字符串 VBA 的 Format 函数与工作表函数 TEXT 用法基本相同,但功能更加强大,许多格式只能用于VBA 的 Format 函数,而不能用于工作表函数 TEXT ,以下是本人归 ...
- Python中用format函数格式化字符串
Python的字符串格式化有两种方式: 百分号方式.format方式 百分号的方式相对来说比较老,而format方式则是比较先进的方式,企图替换古老的方式,目前两者并存. 1.百分号方式 语法:%[( ...
- C#定义类型转化 及 格式化字符串
operator 关键字 operator 关键字用来重载内置运算符,或提供类/结构声明中的用户定义转换.它可以定义不同类型之间采用何种转化方式和转化的结果. operator用于定义类型转化时可采用 ...
- 关于printf错用格式化字符串导致double和long double输出错误的小随笔
[题外话] 以前用HUSTOJ给学校搭建Online Judge,所有的评测都是在Linux下进行的.后来为了好往学校服务器上部署,所以大家重新做了一套Online Judge,Web和Judge都是 ...
- .NET中DateTime.Now.ToString的格式化字符串
.NET中DateTime.Now.ToString显示毫秒:DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") DateTime.N ...
- Python格式化字符串~转
Python格式化字符串 在编写程序的过程中,经常需要进行格式化输出,每次用每次查.干脆就在这里整理一下,以便索引. 格式化操作符(%) "%"是Python风格的字符串格式化操作 ...
- %----format 格式化字符串---- 生成器---- 迭代器
%方式格式化字符串 顺序传参数 o转换8进制x转换十六进制 tp1 = "i am %s" % "alex"tp2 = "i am %s age %d ...
- 飘逸的python - 增强的格式化字符串format函数
自python2.6开始,新增了一种格式化字符串的函数str.format(),可谓威力十足.那么,他跟之前的%型格式化字符串相比,有什么优越的存在呢?让我们来揭开它羞答答的面纱. 语法 它通过{}和 ...
- Python格式化字符串和转义字符
地址:http://blog.chinaunix.net/uid-20794157-id-3038417.html Python格式化字符串的替代符以及含义 符 号 说 明 ...
- [转]:C#的ToString如何格式化字符串
C 货币 2.5.ToString("C") ¥2.50 D 十进制数 25.ToString("D5") 00025 E 科学型 25000.ToString ...
随机推荐
- Xamarin 基础知识
Xamarin 跨平台处理: C#: if (Device.OS == TargetPlatform.Android) { Code…… } else if (Device.OS == TargetP ...
- PythonDay03
## 第三章 ### 今日内容 1.整型 2.布尔值 3.字符串 索引 切片 步长 字符串的方法 4.for循环 ### 1.整型 - python3:全部是整形- python2:整形,长 ...
- 48.QT-网络通信讲解1
网络概念 MAC地址(硬件地址) 网络IP地址(如192.168.1.101) 网络端口(实现多路通信,用来给不同应用程序来区分使用,范围0~65535,比如浏览网页服务(80端口), FTP服务(2 ...
- [ubuntu][deepin]系统增加自定义开机启动项
[ubuntu][deepin]系统增加自定义开机启动项 进行配置 cd /etc/init.d/ ls vim myScript nginx实例 #! /bin/sh # chkconfig: # ...
- [Spring cloud 一步步实现广告系统] 16. 增量索引实现以及投送数据到MQ(kafka)
实现增量数据索引 上一节中,我们为实现增量索引的加载做了充足的准备,使用到mysql-binlog-connector-java 开源组件来实现MySQL 的binlog监听,关于binlog的相关知 ...
- 【C++】string::substr函数
形式:s.substr(p, n) 返回一个string,包含字符串s中从p开始的n个字符的拷贝(p的默认值是0,n的默认值是s.size() - p,即不加参数会默认拷贝整个s) int main( ...
- Nacos(一):Nacos介绍
前言 6月份阿里开源的Nacos出了1.0.1版本,从去年7月份第一个release版本到现在一直在默默关注 官方的版本规划为:Nacos从0.8.0开始支持生产可用,1.0版本可大规模生产可用,2. ...
- 四六级成绩还可以这样查?Python助你装B一步到位!!!
昨天有很多同学在朋友圈秀六级成绩 一个个都如(sang)此(jin)优(tian)秀(liang) 当然也有悲催的哥们 对于上面这位老弟 我只能说:骚呢,兄弟 这种事都能赶上,必须点赞 一.需求分析 ...
- linux_密钥
使用密钥文件. 这里假设主机A(192.168.100.3)用来获到主机B(192.168.100.4)的文件. 在主机A上执行如下命令来生成配对密钥: ssh-keygen -t r ...
- .NET Core 小程序开发零基础系列(1)——开发者启用并牵手成功
最近几个月本人与团队一直与小程序打交道,对小程序的实战开发算比较熟悉,也因一些朋友经常问我各种小程序问题,无不能一一回答,想了很久,决定还是空余时间来写写文章吧,偶尔发现一个人安静的时候写文章特爽 ...