首页 > 代码库 > C语言格式化输出函数及使用禁区

C语言格式化输出函数及使用禁区

编译环境:
Debian: 7.6
gcc: 4.7.2
一、格式化输出函数
C语言中设计到的标准格式化输出函数如下:
#include <stdio.h>
int printf(const char *format, ...);
int fprintf(FILE *stream, 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 vsprintf(char *str, const char *format, va_list ap);
int vsnprintf(char *str, size_t size, const char *format, va_list ap);

二、相互之间的关系
1、共同点
在格式化字串的控制下,把数据输出到输出设备。格式化字串指定如何把后续的参数进行转化。如:
printf("%02x", 100);    /* 输出是16进制 */
printf("%dx", 100);     /* 输出是10进制 */

2、不同点
按输出到的设备分:
printf()
vprintf()
输出至标准输出流stdout
fprintf()
vfprintf()
输出至指定输出流
sprintf()
snprintf()
vsprintf()
vsnprintf()
输出至字串str

按是否指定操作字节分:
snprintf()
vsnprintf()
写入至多size个字节(含‘\0‘)到dtr
vprintf()
vfprintf()
vsprintf()
vsnprintf()
分别等同于:printf()
fprintf()
sprintf()
snprintf()
区别是:它们都有va_list列表,一旦调用完毕,ap即成为“未定义”状态

3、返回值
正常情况下,函数返回输出成功的字符个数(不含‘\0‘)。若出错,返回负值。
snprintf()和vsnprintf()输出字符个数不超过size字节(含‘\0‘),
如果size小于字符串的长度(不含‘\0‘),字符串会被截断,返回值是字符串的长度(不含‘\0‘),如下:
int  len;
char str_buf[100];
len = snprintf(buf, 10, "%s$", "1234567890123");
// len: 13, buf: "1234567890"

如果size大于数据缓冲区长度,缓冲区长度个字串会被复制,返回值是字符串的长度(不含‘\0‘),如下:
char str_buf[5];
// len: 13, buf: "12345"
       

三、使用注意
1、在使用sprintf()和vsprintf()的时候,应确保数据大小不超出str缓冲区的大小,否则产生溢出,造成灾难。如果无法保证,应该选用snprintf()和vsnprintf()。

2、把字串加到原字串的后面,如下:
sprintf(buf, "%s append text", buf);
在我用的gcc中,这种使用如期运行;但是,在有的gcc版本中,结果并不正确。所以,不要这样做。
而且,标准中明确指出,调用sprintf(), snprintf(), vsprintf(),vsnprintf()函数,如果源、目标地址重叠,结果是未定义的(undefined).
  
3、像这样的代码printf(foo);往往会引入bug。因为如果foo中包含“%n”将引起printf()对内存的写入并造成安全漏洞。例如:
printf("%n");    // Segmentation fault

C语言格式化输出函数及使用禁区