字符串函数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 容易出错的地方的主要内容,如果未能解决你的问题,请参考以下文章