关于sprintf和snprintf的比较

#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中却会出现
*/




你可能感兴趣的:(关于sprintf和snprintf的比较)