C语言:跨平台环境下使用snprintf,vsnprintf系列函数要注意返回值的问题

标准C语言函数snprintf,vsnprintf系列函数可以向指定的缓冲区输出格式化打印的字符串。
如果指定的缓存区足够大,那么调用正常,返回值就是写入缓存区的字节长度(不含结尾'\0')
那么缓存区不够大的情况呢?
本文要说的是这系列函数的在缓存区长度不足以输出所有内容时的返回值在不同一编译器提供的实现表现是不同的。
我们用如下一段简单的测试代码来验证其返回值表现。

test_snprintf.c

#include <stdio.h>
#include <errno.h>
#include <string.h>

int main()
{
	char buf[4] = { 0xff };
	int wsz = snprintf(buf,sizeof buf,"hello");
	printf("buf=%s,write size %d\n",buf,wsz);
	if(wsz < 0)
	{
		printf("snprintf ERROR %d:%s\n", errno, strerror(errno));
	}
	else if(wsz >= sizeof buf ) 
	{
		printf("snprintf buffer overflow\n");
	}
	/** 16进制输出缓冲区内容 */	
	for(int i = 0; i < sizeof buf; ++i)
	{
		printf("%x,",buf[i]);
	}
	printf("\n");	
}

上面代码中长度为4字节的buffer显然是无法完整输出hello,分别在MinGW(GCC 5.2.0)和MSVC(vs2015)
编译上面的代码。并运行它们,如下是运行结果。

在这里插入图片描述
在这里插入图片描述

因为输出的内容超过了buffer大小从运行结果看buf中的结果是不一致的,返回值也是不一样的。

在MSVC下返回的是待输出字符串('hello')的大小,而GCC下则是-1,
这不会吧?都是遵循C语言标准,为什么会出现不同的结果?

其实吧,标准这东西就是技术巨头们互相谈判妥协的结果。这两种不同的返回值都符合C语言标准,因为标准就是Microsoft这些巨头们制定的,当返回结果不一样又互不妥协的时候,那就把两种结果都写进标准吧。

下面是C语言标准库(C standard library)关于vsnprintf,snprintf函数的说明原文
https://en.cppreference.com/w/c/io/fprintf
https://en.cppreference.com/w/c/io/vfprintf

下面的截图红框标注的部分为snprintf函数返回值定义:
在这里插入图片描述

翻译出来就是如果输入参数bufsz(缓冲区大小)为0,则返回应该写入buffer的长度(不含结尾’\0’),如果出错返回负值

下面的截图红框标注的部分为vsnprintf函数返回值定义:

在这里插入图片描述

翻译出来就是如果成功返回写入buffer的字符数量,如果出错返回负值。如果因为buffer长度限制而输出结果被截断,则函数返回应该写入buffer的字符数量,前提是这个buffer长度限制不是强制的
(if the limit was not imposed这一句我理解为如果buffer长度为0就是强制的 )。

把上面两段英文原文结合着看就差不多明白了。
GNU的实现的逻辑就是只要buffer长度不足,就认为是出错了,输出-1,然后把标准错误代码 errno 置为ERANGE(34)(不会把buffer最后一字节设置为‘\0’结尾,这样无结尾的字符串很危险了)。
MSC的实现逻辑是,不管buffer长度是多少,都不认为是出错,调用者可以通过返回值是不是超过了buffer的大小来判断是否完整输出(不论怎样会把buffer最后一字节设置为‘\0’结尾)。

这两种逻辑都是遵循C语言标准库定义的。

所以前面的测试代码进一步可以如下完善就可以在跨平台使用场景中更加安全的判断输出缓冲区是否不足了:

#include <stdio.h>
#include <errno.h>
#include <string.h>

int main()
{
	char buf[4] = { 0xff };
	int wsz = snprintf(buf,sizeof buf,"hello");
	printf("buf=%s,write size %d\n",buf,wsz);
	if(wsz < 0)
	{
		/** GNU C 下输出内容超过缓冲区大小要通过写入长度是否小于0以及errno是否为ERANGE来判断 */
		if(errno == EREANGE)
		{
			printf("snprintf buffer overflow\n");
		}else
		{
			/** 其他错误 */
			printf("snprintf ERROR %d:%s\n", errno, strerror(errno));
		}
	}
	else if(wsz >= sizeof buf ) 
	{
		/** MSC 下输出内容超过缓冲区大小通过写入长度是否buffer长度来判断 */
		printf("snprintf buffer overflow\n");
	}		
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
snprintf函数是一个用于格式化字符串的函数,它的原型如下: ```c int snprintf(char *str, size_t size, const char *format, ...); ``` 其中参数的含义如下: - `str`:指向目标字符串的指针,即格式化后的结果将被存储在这个字符串中。 - `size`:目标字符串的最大容量,即`str`指向的字符数组的大小。 - `format`:格式化字符串,指定了输出的格式。 - `...`:可变参数列表,用于填充`format`中的占位符。 `snprintf`函数的功能是将格式化后的字符串按照指定的格式写入目标字符串中,直到达到最大容量或者遇到空字符为止。如果最终生成的字符串长度超过了最大容量,那么超出部分将被截断。 下面是一个示例代码,展示了如何使用`snprintf`函数: ```c #include <stdio.h> int main() { char str[20]; int num = 123; float f = 3.14; // 将格式化后的字符串存储到str中 int result = snprintf(str, sizeof(str), "Number: %d, Float: %.2f", num, f); printf("Formatted string: %s\n", str); printf("Number of characters written: %d\n", result); return 0; } ``` 输出结果如下: ``` Formatted string: Number: 123, Float: 3.14 Number of characters written: 24 ``` 在上面的示例中,我们使用`snprintf`函数将一个整数和一个浮点数格式化为字符串,并将结果存储在`str`中。`sizeof(str)`用于指定`str`数组的最大容量。最终生成的字符串为"Number: 123, Float: 3.14",长度为24,这个值被存储在`result`变量中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

10km

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值