zoukankan      html  css  js  c++  java
  • C/C++> 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。

    参考微软官方文档strcpy、wcscpy、_mbscpy

    下面的程序,编译器将产生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(源字符串)拷贝的字符个数。

    参考微软官方文档strncpy、_strncpy_l、wcsncpy、_wcsncpy_l、_mbsncpy、_mbsncpy_l

    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字符()
    #pragma warning(suppress : 4996)
    strncpy(s, "rr", 3); // "rr" C4996
    
    printf("%s
    ", 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、wcscpy_s、_mbscpy_s、_mbscpy_s_l

    要正常使用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
    ", s1);
    

    4. strncpy_s

    strcpy_s与strncpy相比较,多了第4个参数指定要拷贝的源字符串字符数。必须为null字符预留目的缓存空间。

    函数尝试将 strSource 的前 D 个字符复制到 strDest,其中 D 是 count 和 strSource 的长度。 如果这些 D 字符将放在 strDest (其大小被指定为 numberOfElements) 并且仍为 null 终止符留下空间,则会复制这些字符,并追加一个终止 null。否则, strDest[0] 设置为 null 字符,并调用无效参数处理程序,如 参数验证中所述。

    参加微软官方文档strncpy_s、_strncpy_s_l、wcsncpy_s、_wcsncpy_s_l、_mbsncpy_s、_mbsncpy_s_l

    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
    ", dst);
    

    总结

    • 4个函数有个共同弱点,就是如果源和目标字符串存在重叠,行为未定义;
    • 使用安全版本库函数(_s后缀)进行字符串拷贝,不要使用不安全版本;
    • 带_s版本字符串拷贝,总能确保拷贝后字符串以null字符结尾;
    • 需要预留null字符空间;
  • 相关阅读:
    编程用外星人宏建设置教程(在网上找了好久没几个相关帖子,自己研究写下来留个备忘吧)
    通过selenium+pyautogui模拟登陆淘宝(完美实现)
    python之字符串的五种拼接方式
    python之批量文件重命名
    爬虫系列之链家的信息爬取及数据分析
    跟潭州学院的强子老师学习网络爬虫---爬取全书网
    Python之编写测试用例,unittest模块中的TestCase类中的六种断言方法,以及setUp()函数。
    Python学习之路
    GridBagLayout的帮助类
    eclipse和cygwin搭建C++环境的修正版本
  • 原文地址:https://www.cnblogs.com/fortunely/p/14515340.html
Copyright © 2011-2022 走看看