【C语言】strncpy函数和strncpy_s函数的不同!关于末尾追加\0

今天在微软家的strncpy_s函数上栽了跟头,记录一下

char *strncpy(char *strDest, const char *strSource, size_t count );
errno_t strncpy_s(char *strDest,  size_t numberOfElements, const char *strSource, size_t count);

简言之,在已经从strSource拷贝了多达count个非0字符后:

  • C标准库的strncpy函数不会strDest追加'\0'
  • 微软家的strncpy_s函数试图往strDest追加'\0'

细说

摘抄一下MSDN上关于strncpy函数的说明:

The strncpy function copies the initial count characters of strSource to strDest and returns strDest.
If count is less than or equal to the length of strSource, a null character is not appended automatically to the copied string.
If count is greater than the length of strSource, the destination string is padded with null characters up to length count. The behavior of strncpy is undefined if the source and destination strings overlap.

翻译过来:
strncpy函数拷贝strSource原始的count个字符到strDest,并且返回strDest
如果count小于等于strSource的长度,不会自动追加一个空字符到拷贝至的字符串

再来看看MSDN上关于strncpy_s函数的说明:

These functions try to copy the first D characters of strSource to strDest, where D is the lesser of count and the length of strSource. If those D characters will fit within strDest (whose size is given as numberOfElements) and still leave room for a null terminator, then those characters are copied and a terminating null is appended; otherwise, strDest[0] is set to the null character and the invalid parameter handler is invoked, as described in Parameter Validation.
……

翻译过来:
这些函数 [ 1 ] ^{[1]} [1]尝试从 strSource 拷贝前D个字符到strDest,其中D是count和strSource中的较小者。如果strDest(它的大小由numberOfElements给出)能够装下那D个字符,并且仍然留有空间来放置一个空的结尾标志,那么那些字符会被拷贝并且会追加一个空的结尾标志;否则的话, strDest[0] 被设置为空字符,并且会调用无效参数处理机制,如 Parameter Validation中所述。

注[1]:在MSDN原文中"这些函数"代指一家子*ncpy_s形式的函数,strncpy_s函数是其中之一

从上面的两段说明可以看出,strncpystrncpy_s的行为是有着很大不同的。

在使用strncpy函数时我们可能习惯于把count设置为目的地址strDest的缓冲区大小,这是没有问题的,因为strncpy函数总是老老实实地就往目的地址拷贝恰好count个字符,如果拷贝过程到达了源字符串的结束标志'\0',后面直接用填充往目的地址若干个'\0',直到写入了满count个字符,否则如果拷贝了多达count个非0字符,就停止拷贝,不会往目的地址追加'\0'

strncpy_s函数则不一样,它总是试图往目的地址追加'\0',如果在调用strncpy_s函数时把count设置为目的地址strDest的缓冲区大小,可能会出错。因为如果源字符串字符个数(不计入结尾的'\0')大于等于count,那么strncpy_s函数发现:如果拷贝count个非0字符并且追加一个'\0',这个追加的'\0'其实就越界了,所以strncpy_s函数不会进行拷贝,并且会抛出异常。

所以在调用strncpy_s函数时,用来说明strDest缓冲区的大小的那个参数numberOfElements必须满足——
numberOfElements ≥ \geq 1+ min(count,strlen(strDest))

示例

已测试于VS2019

分析下面的示例程序

#include 
#include 

int main()
{
	char buff[11] = "cccccccccc";	//15个'c'
	strncpy(buff, "hello", 5);			//buff内容变为helloccccc\0
	printf("%s\n", buff);				//打印helloccccc
	strncpy_s(buff, 6, "world", 5);		//buff内容为world\0cccc\0
	printf("%s\n", buff);				//打印world
	strncpy_s(buff, 5, "abcde", 5);		//抛出异常
	return 0;
}

刚开始buff缓冲区的内容为

偏移 0 1 2 3 4 5 6 7 8 9 10
内容 ‘c’ ‘c’ ‘c’ ‘c’ ‘c’ ‘c’ ‘c’ ‘c’ ‘c’ ‘c’ ‘\0’

第7行的strncpy函数调用拷贝’h’、‘e’、‘l’、‘l’、'o’共5个字符到buff,不会补'\0'
这时buff缓冲区的内容为

偏移 0 1 2 3 4 5 6 7 8 9 10
内容 ‘h’ ‘e’ ‘l’ ‘l’ ‘o’ ‘c’ ‘c’ ‘c’ ‘c’ ‘c’ ‘\0’

第8行的printf会打印helloccccc

第9行的strncpy_s函数调用拷贝了’w’、‘o’、‘r’、‘l’、'd’这5个字符串后,还追加了一个'\0',实际上是往buff写入了6个字节。
这时buff缓冲区的内容为

偏移 0 1 2 3 4 5 6 7 8 9 10
内容 ‘w’ ‘o’ ‘r’ ‘l’ ‘d’ ‘\0’ ‘c’ ‘c’ ‘c’ ‘c’ ‘\0’

第10行的printf会打印world

第11行的strncpy_s函数调用,由于abcde加上一个'\0'有6个字符,超过了指定的缓冲区大小5,所以strncpy_s不会进行拷贝,并且会抛出异常。

【C语言】strncpy函数和strncpy_s函数的不同!关于末尾追加\0_第1张图片
弹出窗口中"Buffer is too small"这句话也直接说明了,抛出这个异常是因为缓冲区太小了(要写入6个字符,而缓冲区大小为5)。

你可能感兴趣的:(【C语言】strncpy函数和strncpy_s函数的不同!关于末尾追加\0)