CC++strcpy,strncpy,strcpy_s,strncpy_s联系与区别
C++中,复制C风格字符串的⽅法有4种:strcpy, strncpy, strcpy_s, strncpy_s。它们有什么区别和联系了?
1. strcpy
strcpy和strncpy是早期C库函数,头⽂件string.h。现在已经发布对应safe版本,也就是strcpy_s, strncpy_s。
strcpy 函数将 strSource(包括终⽌ null 字符)复制到 strDestination 指定的位置。如果源和⽬标字符串重叠,则 strcpy 的⾏为是不确定的。
注意:strcpy不安全的原因
由于 strcpy 在复制 strSource 之前不检查 strDestination 中是否有⾜够的空间,因此它可能会导致缓冲区溢出。因此,我们建议使⽤ strcpy_s。
参考微软官⽅⽂档
下⾯的程序,编译器将产⽣C4996警告/错误提⽰
char p1[] = "hello";
char* ps = new char[100];
// 如果想忽略C4996,VS2019下⾯,需要⽤编译器指令
// #pragma warning(suppress : 4996)
strcpy(ps, p1); // C4996
2. strncpy
strncpy 函数将 strSource 的初始计数字符复制到 strDest 并返回 strDest。如果 count ⼩于或等于 strSource 的长度,则不会⾃动向复制的字符串追加 null 字符。如果 count ⼤于 strSource 的长度,则⽬标字符串将⽤空字符填充,最⼤长度为 count。如果源和⽬标字符串重叠,则 strncpy 的⾏为是不确定的。
同strcpy⼀样,strncpy也是不安全的
strncpy 不检查 strDest 中是否有⾜够的空间;这会导致缓冲区溢出的潜在原因。 Count 参数限制复制的字符数;它不是 strDest 的⼤⼩限制。
strncpy和strcpy的主要区别:strncpy多了⼀个参数count(第3个参数),可以指定要从strSource(源字符串)拷贝的字符个数。
参考微软官⽅⽂档
char s[20];
strcpy_s(s, sizeof(s), "AA BB CC"); // "AA BB CC"
// 2 <= "tt"字符串长度, 将"tt"拷贝并覆盖s[0..1], 不影响后⾯的元素
#pragma warning(suppress : 4996)
strncpy(s, "tt", 2); // "tt BB CC" C4996
// 3 > "rr"字符串长度, ⾃动添加null字符(\0)
#pragma warning(suppress : 4996)
strncpy(s, "rr", 3); // "rr" C4996
printf("%s\n", s);
3. strcpy_s
strcpy_s是strcpy的安全版本,通过第⼆个参数dest_size限制使⽤⽬的缓存⼤⼩,对缓存⼤⼩、源字符串长度、要使⽤的缓存⼤⼩都做了安全检查,避免溢出。
strcpy_s 函数将 src 地址中的内容(包括终⽌ null 字符)复制到 dest 指定的位置。⽬标字符串必须⾜够⼤以保存源字符串及其结尾的 null 字符。如果源和⽬标字符串重叠,则 strcpy_s 的⾏为不确定。
如果 dest 或 src 为空指针,或者如果⽬标字符串的⼤⼩ dest_size 太⼩,则调⽤⽆效参数处理程序,如参数验证中所述。如果允许执⾏继续,则当 dest 或 src 为 null 指针时,这些函数将返回 EINVAL ,并将 errno 设置为 EINVAL ; 当⽬标字符串过⼩时,它们将返回 ERANGE 并将 errno 设置为 ERANGE 。
成功执⾏时,⽬标字符串始终以 null 结尾。
参考微软官⽅⽂档
要正常使⽤strcpy_s进⾏字符串拷贝,必须要求sizeof(s1)(缓存⼤⼩) >= dest_size(限制使⽤⽬的缓存⼤⼩) > strlen(src)(源字符串长度(不包括null字符))。另外dest_size长度,理论上不能超过RSIZE_MAX。
char s1[5];
//  sizeof(s1) >= dest_size > strlen(src)
strcpy_s(s1, 4, "AA"); // "AA"
//  sizeof(s1) >= dest_size <= strlen(src)
strcpy_s(s1, 1, "AA"); // 程序异常退出
//  sizeof(s1) >= dest_size > strlen(src)
strcpy_s(s1, sizeof(s1), "AA"); // "AA"
strcpy_s(s1, sizeof(s1), "AA B"); // "AA B"
//  dest_size <= strlen(src)
strcpy_s(s1, sizeof(s1), "AA BB"); // s1缓存⼤⼩ <= 源字符串长度(不包括null字符), 程序异常退出
// sizeof(s1) < dest_size
strcpy_s(s1, 6, "A"); // dest_size > sizeof(s1), 程序异常退出  C6386
printf("%s\n", s1);
4. strncpy_s
strcpy_s与strncpy相⽐较,多了第4个参数指定要拷贝的源字符串字符数。必须为null字符预留⽬的缓存空间。
函数尝试将 strSource 的前 D 个字符复制到 strDest,其中 D 是 count 和 strSource 的长度。如果这些 D 字符将放在 strDest (其⼤⼩被指定为 numberOfElements) 并且仍为 null 终⽌符留下空间,则会复制这些字符,并追加⼀个终⽌ null。否则, strDest[0]设置为 null 字符,并调⽤⽆效参数处理程序,如参数验证中所述。
c++求字符串长度参加微软官⽅⽂档
char dst[5];
//strncpy_s(dst, 5, "a long string", 5); // 将源字符串5个字符拷贝到⽬的缓存5byte长度, 没有预留null字符空间, 程序异常退出
strncpy_s(dst, 5, "a long string", _TRUNCATE); // 截断 "a lo" , 等价于下⾯的语句
strncpy_s(dst, 5, "a long string", 4); // "a lo"
printf("%s\n", dst);
总结
4个函数有个共同弱点,就是如果源和⽬标字符串存在重叠,⾏为未定义;
使⽤安全版本库函数(_s后缀)进⾏字符串拷贝,不要使⽤不安全版本;
带_s版本字符串拷贝,总能确保拷贝后字符串以null字符结尾;
需要预留null字符空间;

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。