字符串函数sprintf / sprintf_s 容易出错的地方

Posted 小哈龙

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了字符串函数sprintf / sprintf_s 容易出错的地方相关的知识,希望对你有一定的参考价值。

其实,用C/C++做开发的童鞋,对sprintf不会陌生,对该函数的一些问题,一直想好好总结下:

如果第2个format(格式)参数中用%指定的后续参数个数与实际参数必须一致,否则可能会出各种问题。所谓一致是严格一致,看几个例子:

1. 参数类型不匹配

如果你用的%d,对应的实参却用double,将得到错误的结果,而且更为严重的是,如果该参数后还有别的参数,将影响后面的参数解析:

char buf[1024];

double dV = 123.0;
int iLen = sprintf( buf, "Test:  %d, not one %d\\n", dV, 1);

std::cout << buf;

并不会输出期望的结果:Test:  123, not one 1

我在VS2013下得到的输出是: Test:  0, not one 1079951360

如果我们知道函数调用和参数传递的机理,就会明白,sprintf 期望format参数后接2个int型参数(都是4字节的),但却给了个double参数和一个常数,我们知道double是8字节的,所以double就被分为了2个int,而后面的常数1压根就没用上,所以结果不可能正确。

结果要正确,必须将double参数强转成int:
 

int iLen = sprintf( buf, "Test:  %d, not one %d\\n", (int)dV, 1);


2. 参数数目不匹配

format中指定的参数数目与实际的数目不一致时,也得不到正确的结果:

int iLen = sprintf( buf, "Test:  %d, not one %d\\n", 1);    // 实参少1个

VS2013下得到的输出:Test:  1, not one -84618255

其实后面显示的数是随机的,就是函数调用栈实参1后面的一个4字节数,这个例子不会导致宕机,因为第2个实参取的是栈上的随机数(未初始化数)。

3. 参数类型不匹配

如果format参数中有%s,则对应的实参一定得给对了,否则极其可能宕机:

char buf[1024];

double dV = 123.0;
int iLen = sprintf( buf, "Test:  %s, not one %d\\n", dV, 1);

std::cout << buf;

在Debug模式下,将断点设置在最后一行,将会发现调用栈完全不对了,因为sprintf将dV的值当字符串地址看待,所以宕掉

char buf[1024];

std::string str = "hello";
int iLen = sprintf( buf, "Test:  %s, not one %d\\n", str, 1);

std::cout << buf;

这个也不会输出正常结果, 会把str对象(copy的对象)的空间当作字符串地址

VS2013下得到的输出:  Test:  (null), not one -858993460 (还算幸运,没宕机)
sprintf不会帮你做字符串转换,你的主动告诉它。


最后,第1个缓冲区参数,需要有足够的空间,否则很容易越界导致各种问题。

sprintf_s在缓冲区后加了个缓冲区大小参数,VS的实现会将缓冲区按缓冲区大小参数memset,所以,如果缓冲区大小参数不正确,又将可能导致新的问题--越界

原文链接:https://blog.csdn.net/Joal_zhu/article/details/38681583

以上是关于字符串函数sprintf / sprintf_s 容易出错的地方的主要内容,如果未能解决你的问题,请参考以下文章

sprintf_s() 因“调试断言失败”错误而失败

sprintf_s 问题

sprintf()函数的用法

sprintf_s的使用

c语言中的sprintf函数?

如何在sprintf函数中输出百分号等特殊符号