如何将当前的 strcpy 转换为 strcpy_s?

Posted

技术标签:

【中文标题】如何将当前的 strcpy 转换为 strcpy_s?【英文标题】:How to convert current strcpy to strcpy_s? 【发布时间】:2017-10-01 01:25:35 【问题描述】:

我有一个大型项目,到处都在使用strcpy。我正在考虑使用strcpy_s 而不是strcpy。我想我已经使用了将近 10,000 次 strcpy。每次strcpy换一个都太麻烦了。有什么有效的转化方式吗?

【问题讨论】:

你的意思是除了显而易见的? (即全局 s&r,然后修复所有损坏的东西)。根据定义,每个损坏的地方都需要检查,所以分支你的代码,看看它有多糟糕。 strcpy_s() 无论如何都是毫无意义的。 由于dest 的大小可以在任何地方声明,自动执行任何操作似乎都非常困难。 一万次!为什么这被标记为 C++? 考虑n1967,它建议“......附件 K 要么从 C 标准的下一个修订版中删除,要么弃用然后删除。”确定要将代码切换到strcpy_s()?建议阅读“常见错误”部分,了解您遇到的问题。 【参考方案1】:

你真的不应该在没有检查的情况下这样做,因为如果没有明智地完成缓冲区管理,就会失去加强缓冲区管理的意义。

由于目标缓冲区的性质(例如静态或堆分配)对于strcpy_s() 的正确参数非常重要,而现有的strcpy() 调用中当然不存在该信息,您必须以任何方式添加它。这需要一个人。

通常像strcpy(dest, src); 这样的调用可以转换为strcpy_s(dest, sizeof dest, src);,但是如果dest 是堆分配的,那么这将只是指针的大小而不是指向缓冲区的大小,这当然是错误的。

【讨论】:

你必须确保dest 被声明为一个数组!因为如果dest 是指针,这通常是一场代码破坏灾难。 为了进一步增加混淆,至少有一个 C++ 供应商 (MS) 提供了一个模板包装器,用于在使用声明的固定目标 char 数组(即 char dst[N]; strcpy_s(dst, src);)时推导出 size_t 参数。因此,根据语言和供应商,将strcpy 替换为strcpy_s 将在此类用例中正确编译,而编译中断的地方将成为dest 参数的实际原始指针。我不知道该感谢他们还是诅咒他们。【参考方案2】:

鉴于您提供了一个无法推断的附加参数 (size_t destsz),该参数需要准确才能从更改中受益,您遇到了真正的问题。

一个有 10,000 次使用 strcpy() 的应用程序听起来很疯狂,但你在哪里。

第一 如果您的时间/资源有限,那么我只能建议进行一些风险评估。 哪些调用正在复制外部数据(来自文件、操作系统、用户、端口或套接字等)。 专注于确保那些不会被覆盖,您将更有效地降低风险。

第二 如果您有任何标准变量名称和标准“最大尺寸”,您也许可以进行一些全局搜索和替换。

假设您在您的平台上经常使用filename 并且文件名最多为255 个字符(加上NUL),您可以将strcpy(filename, 替换为(比如说)strcpy_s(filename,FILENAME_MAX_SZ

如果代码“到处都是”,你就需要做很多工作。

strcpy(v, 替换为strcpy_s(v,SIZE_MAX(使用正则表达式)是一个肤浅的问题,除了可能潜入您的组织代码质量脚本之外,实际上并没有为您带来任何好处。我没有告诉你这样做! ;)

第三 如果您想在 C11 _Generic 的世界中漫步,您可以尝试以下方式:

#define __STDC_WANT_LIB_EXT1__

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int strcpy_s(char *dest,size_t destsz,const char *src)
    if(strlen(src)>=destsz)
        return 1;
    
    strcpy(dest,src);
    return 0;


char *d_strcpy(char *dest,const char *src)
#ifndef NDEBUG
 fprintf(stdout,"unsafe copy of %s\n",src);
#endif
    return strcpy(dest,src);



#define strcpy(dest,src) _Generic (dest,\
    char[100] : strcpy_s(dest,sizeof dest,src),\
    char*: d_strcpy(dest,src)\
    )

int main(void) 
    char a[100]='A','B','\0';
    char *b=malloc(10*sizeof(char));

    strcpy(a,"XXX");
    strcpy(b,"XYX");

    printf("%s %s\n",a,b);

    free(b);
    return 0;

不幸的是,您确实需要指定数组大小,因此可能需要使用有限的“最大大小”列表,虽然这应该适用于 Clang(未经测试),但它在 GCC 上失败,因为他们不同意如何解决控制型!见Document: N1930 (controlling expression of _Generic)

狩猎愉快。

【讨论】:

控制类型的东西已经商定。 GCC 是对的。【参考方案3】:

为什么要替换 strcpy() 函数? There is nothing wrong with strcpy。教条地将其更改为 strcpy_s 不会解决任何问题。您需要做的是考虑每个案例:

源缓冲区的大小是否已知并且其内容是否经过验证? 源缓冲区甚至是动态的吗?它的大小可以变化吗?或者您知道编译时的最大大小吗? 目标缓冲区的大小是否足够大?它是否完全指向分配的内存?

这不是 strcpy 甚至字符串独有的东西,而是代码中每个 数组 都必须考虑的东西。

【讨论】:

以上是关于如何将当前的 strcpy 转换为 strcpy_s?的主要内容,如果未能解决你的问题,请参考以下文章

strcpy_s 是如何工作的?

c语言strcpy将一个结构体的数据复制到另一个后,出问题了

将 strcpy_s 用于 TCHAR 指针(Microsoft 特定)

1_14 实现strcpy函数

在Qt中如何将QString转换为const char*

strcpy&memcpy理解