#include <stdio.h>
#include <string.h>
typedef unsigned char uchar;
#define BUF_SIZE 10 // 缓冲区大小
#define CLEAR_SIZE (BUF_SIZE+2) // 操作的缓冲区大小, 需要全部重置
void printRuler(int len)
{
putchar('\n');
for(int i = 1; i <= len; i++)
{
printf("%02d ", i);
if(i == BUF_SIZE)
printf("\t|\t");
}
putchar('\n');
}
void dis(char *buf, int len)
{
for(int i = 0; i < len; i++)
{
printf("%02x ", (uchar)buf[i]);
if(i == BUF_SIZE - 1)
printf("\t|\t");
}
// puts("\n"); // 另起一行输出字符串并换行, 不会接在原来的后面
putchar('\n'); // 直接输出字符
printf("buf=[%s]\n\n", buf);
}
int main()
{
char buf[BUF_SIZE];
memset(buf, 0xcc, CLEAR_SIZE);
dis(buf, CLEAR_SIZE);
printRuler(CLEAR_SIZE);
printf("-------------------------------------- snprintf\n");
memset(buf, 0xcc, CLEAR_SIZE);
snprintf(buf, BUF_SIZE, "%s", "12345678"); // 未溢出
dis(buf, CLEAR_SIZE);
memset(buf, 0xcc, CLEAR_SIZE);
snprintf(buf, BUF_SIZE, "%s", "123456789"); // 未溢出, 刚好填满
dis(buf, CLEAR_SIZE);
memset(buf, 0xcc, CLEAR_SIZE);
snprintf(buf, BUF_SIZE, "%s", "123456789A"); // 未溢出, 被截断1个字符
dis(buf, CLEAR_SIZE);
memset(buf, 0xcc, CLEAR_SIZE);
snprintf(buf, BUF_SIZE, "%s", "123456789AB"); // 未溢出, 被截断2个字符
dis(buf, CLEAR_SIZE);
//////////////////////////////////////////////////////////////////////////////
printRuler(CLEAR_SIZE);
printf("-------------------------------------- sprintf\n");
memset(buf, 0xcc, CLEAR_SIZE);
sprintf(buf, "%s", "12345678"); // 未溢出
dis(buf, CLEAR_SIZE);
memset(buf, 0xcc, CLEAR_SIZE);
sprintf(buf, "%s", "123456789"); // 刚刚好
dis(buf, CLEAR_SIZE);
memset(buf, 0xcc, CLEAR_SIZE);
sprintf(buf, "%s", "123456789A"); // 溢出1个字符
dis(buf, CLEAR_SIZE);
memset(buf, 0xcc, CLEAR_SIZE);
sprintf(buf, "%s", "123456789AB"); // 溢出2个字符
dis(buf, CLEAR_SIZE);
return 0;
}
/*
运行情况:
D:\profile\Desktop\test>make
g++ -o a.exe a.cpp
D:\profile\Desktop\test>a
cc cc cc cc cc cc cc cc cc cc | cc cc
buf=[烫烫烫烫烫烫@]
01 02 03 04 05 06 07 08 09 10 | 11 12
-------------------------------------- snprintf
31 32 33 34 35 36 37 38 00 cc | cc cc
buf=[12345678]
31 32 33 34 35 36 37 38 39 00 | cc cc
buf=[123456789]
31 32 33 34 35 36 37 38 39 00 | cc cc
buf=[123456789]
31 32 33 34 35 36 37 38 39 00 | cc cc
buf=[123456789]
01 02 03 04 05 06 07 08 09 10 | 11 12
-------------------------------------- sprintf
31 32 33 34 35 36 37 38 00 cc | cc cc // 空间有余
buf=[12345678]
31 32 33 34 35 36 37 38 39 00 | cc cc // 刚好填充満
buf=[123456789]
31 32 33 34 35 36 37 38 39 41 | 00 cc // 溢出1个字符
buf=[123456789A]
31 32 33 34 35 36 37 38 39 41 | 42 00 // 溢出2字符
buf=[123456789AB]
结论:
1. sprintf和snprintf都会在字符串末尾加上'\0'
2. snprintf比sprintf安全,即不会造成缓冲区溢出
*/
以上的测试环境是GNUMake(windows), 和Linux.
===============================================================================
以下的代码测试环境是windows vc6和vc2010
#define _CRT_SECURE_NO_WARNINGS // 针对vc2010添加
#include <stdio.h>
#include <string.h>
typedef unsigned char uchar;
#ifdef WIN32
#define snprintf _snprintf // windows平台无snprintf, 但是有_snprintf
#endif
#define BUF_SIZE 10 // 缓冲区大小
#define CLEAR_SIZE (BUF_SIZE+2) // 操作的缓冲区大小, 需要全部重置
void printRuler(int len)
{
putchar('\n');
for(int i = 1; i <= len; i++)
{
printf("%02d ", i);
if(i == BUF_SIZE)
printf("\t|\t");
}
putchar('\n');
}
void dis(char *buf, int len)
{
for(int i = 0; i < len; i++)
{
printf("%02x ", (uchar)buf[i]);
if(i == BUF_SIZE - 1)
printf("\t|\t");
}
// puts("\n"); // 另起一行输出字符串并换行, 不会接在原来的后面
putchar('\n'); // 直接输出字符
printf("buf=[%s]\n\n", buf);
}
int main()
{
char buf[BUF_SIZE];
memset(buf, 0xcc, CLEAR_SIZE);
dis(buf, CLEAR_SIZE);
printRuler(CLEAR_SIZE);
printf("-------------------------------------- snprintf\n");
memset(buf, 0xcc, CLEAR_SIZE);
snprintf(buf, BUF_SIZE, "%s", "12345678"); // 未溢出
dis(buf, CLEAR_SIZE);
memset(buf, 0xcc, CLEAR_SIZE);
snprintf(buf, BUF_SIZE, "%s", "123456789"); // 未溢出, 刚好填满
dis(buf, CLEAR_SIZE);
memset(buf, 0xcc, CLEAR_SIZE);
snprintf(buf, BUF_SIZE, "%s", "123456789A"); // 未溢出, 被截断1个字符
dis(buf, CLEAR_SIZE);
memset(buf, 0xcc, CLEAR_SIZE);
snprintf(buf, BUF_SIZE, "%s", "123456789AB"); // 未溢出, 被截断2个字符
dis(buf, CLEAR_SIZE);
//////////////////////////////////////////////////////////////////////////////
printRuler(CLEAR_SIZE);
printf("-------------------------------------- sprintf\n");
memset(buf, 0xcc, CLEAR_SIZE);
sprintf(buf, "%s", "12345678"); // 未溢出
dis(buf, CLEAR_SIZE);
memset(buf, 0xcc, CLEAR_SIZE);
sprintf(buf, "%s", "123456789"); // 刚刚好
dis(buf, CLEAR_SIZE);
memset(buf, 0xcc, CLEAR_SIZE);
sprintf(buf, "%s", "123456789A"); // 溢出1个字符
dis(buf, CLEAR_SIZE);
memset(buf, 0xcc, CLEAR_SIZE);
sprintf(buf, "%s", "123456789AB"); // 溢出2个字符
dis(buf, CLEAR_SIZE);
return 0;
}
/*
运行情况(vc6/vc2010):
cc cc cc cc cc cc cc cc cc cc | cc cc
buf=[烫烫烫烫烫烫?]
01 02 03 04 05 06 07 08 09 10 | 11 12
-------------------------------------- snprintf
31 32 33 34 35 36 37 38 00 cc | cc cc // 未溢出时会填充字符串结束符('\0')
buf=[12345678]
31 32 33 34 35 36 37 38 39 00 | cc cc // 未溢出时会填充字符串结束符('\0')
buf=[123456789]
31 32 33 34 35 36 37 38 39 41 | cc cc // 溢出了将不会填充字符串结束符('\0')
buf=[123456789A烫?]
31 32 33 34 35 36 37 38 39 41 | cc cc // 溢出了将不会填充字符串结束符('\0')
buf=[123456789A烫?]
01 02 03 04 05 06 07 08 09 10 | 11 12
-------------------------------------- sprintf
31 32 33 34 35 36 37 38 00 cc | cc cc
buf=[12345678]
31 32 33 34 35 36 37 38 39 00 | cc cc
buf=[123456789]
31 32 33 34 35 36 37 38 39 41 | 00 cc
buf=[123456789A]
31 32 33 34 35 36 37 38 39 41 | 42 00
buf=[123456789AB]
结论:
1. windows上无snprintf,但是有_snprintf可以达到同样的功能,但是细节之处略有不同
2. 未溢出时,sprintf和snprintf都会在字符串末尾加上'\0';
3. 超出缓冲区大小时,_snprintf不会造成溢出,但是不会在缓冲区中添加字符结束符
4. sprintf始终会在字符串末尾添加结束符,但是存在缓冲区溢出的情况
5. _snprintf比sprintf安全,即不会造成缓冲区溢出
6. vc6中对于_snprintf缓冲区溢出时不会出现崩溃信息,但是在vc2010中却会出现
*/