代码之家  ›  专栏  ›  技术社区  ›  lornova

我应该使用哪些字符串操作函数?

  •  16
  • lornova  · 技术社区  · 14 年前

    在我的Windows/Visual C环境中,有很多方法可以用来执行相同的基本字符串操作任务。

    例如,对于执行字符串复制,我可以使用:

    • strcpy ,ANSI C标准库函数(CRT)
    • lstrcpy ,kernel32.dll中包含的版本
    • StrCpy ,来自Shell轻量级实用程序库
    • StringCchCopy / StringCbCopy ,来自“安全字符串”库
    • strcpy_s ,CRT的安全增强版本

    虽然我知道所有这些选择都有历史原因,但我可以为新代码选择一组一致的函数吗?哪一个呢?还是应该逐个选择最合适的函数?

    6 回复  |  直到 14 年前
        1
  •  13
  •   Wizard    14 年前

    首先,让我们回顾一下每个功能集的优缺点:

    ANSI C标准库函数(CRT)

    功能如下 strcpy 是开发可移植C代码的唯一选择。即使是在一个只支持Windows的项目中,将可移植代码与依赖于操作系统的代码分离也是一件明智的事情。
    这些函数通常具有组装级优化,因此速度非常快。
    有一些缺点:

    • 它们有许多限制,因此通常您仍然需要从其他库调用函数或提供自己的版本
    • 有一些古语,像臭名昭著的 strncpy

    Kernel32字符串函数

    功能如下 lstrcpy 由kernel32导出,仅当试图避免对CRT的任何依赖时才应使用。可能有两个原因:

    • 为超轻量的可执行文件避免CRT负载(现在不常见,但不是10年前!)
    • 避免初始化问题(如果使用 CreateThread 而不是 _beginthread ).

    此外,kernel32函数 能够 比CRT版本更优化:当你的可执行文件在Windows 9上运行时,为核心i13优化,kernel32 能够 使用程序集优化版本。

    Shell轻量级实用函数

    以下是对kernel32函数的相同考虑,以及一些更复杂函数的附加值。但我怀疑他们是否得到了积极的维护,我会跳过他们。

    StrSafe函数

    这个 StringCchCopy / StringCbCopy 函数通常是我个人的选择:它们设计得非常好,功能强大,而且速度惊人(我还记得一篇白皮书,将这些函数的性能与CRT等价物进行了比较)。

    安全增强型CRT功能

    这些函数的优点毋庸置疑是非常类似于ANSI C的等价物,因此移植遗留代码是小菜一碟。我特别喜欢基于模板的版本(当然,仅当编译为C++)时才可用。我真的希望它们最终会标准化。不幸的是,它们有许多缺点:

    • 虽然这是一个提议的标准,但它们基本上被非Windows社区拒绝了(可能只是因为它们来自微软)
    • 失败时,它们不仅返回错误代码,而且执行无效的参数处理程序

    结论

    虽然我个人最喜欢Windows开发的是StrSafe库,但我的建议是尽可能使用ANSI C函数,因为可移植代码总是一件好事。

    在现实生活中,我开发了一个个性化的可移植库,其原型类似于增强安全性的CRT函数(包括强大的基于模板的技术),它依赖于Windows上的StrSafe库和其他平台上的ANSI C函数。

        2
  •  4
  •   JaredPar    14 年前

    对于新项目和现有项目,我个人的偏好是 StringCchCopy/StringCbCopy 安全字符串库中的版本。我发现这些功能总体上是非常一致和灵活的。他们是从集团设计的,考虑到了安全性。

        3
  •  3
  •   Ajay    8 年前

    我会回答这个问题稍微有点不同。你想不想有可移植的代码?如果你想便携,除了 strcpy , strncpy ,或标准的宽字符“字符串”处理函数。

    如果你的代码必须在Windows下运行,你可以使用“安全字符串”变量。

    如果您希望是可移植的并且仍然希望有一些额外的安全性,那么您应该检查跨平台库,例如 glib libapr 或其他“安全字符串库”,例如: SafeStrLibrary

        4
  •  1
  •   Alan Haggai Alavi    14 年前

    我建议使用 标准库 ,或函数 跨平台库 .

        5
  •  0
  •   J V    14 年前

    我会坚持一个,我会选择最有用的库中的任何一个,以防您需要更多的使用它,我会远离kernel32.dll,因为它只是windows。

    但这些只是提示,这是一个主观问题。

        6
  •  0
  •   R.. GitHub STOP HELPING ICE    14 年前

    在这些选择中,我只会使用 strcpy . 至少 strcpy_s lstrcpy 是不应该使用的东西。研究那些独立编写的库函数可能是值得的,但我不太愿意把非标准的库代码当作字符串安全的灵丹妙药。

    如果你在使用 拷贝字符串 ,您需要确保字符串适合目标缓冲区。如果你只给它分配了大小 strlen(source)+1 ,只要源字符串不同时受到另一个线程的修改,就可以了。否则需要测试它是否适合缓冲区。您可以使用如下接口 snprintf strlcpy (非标准的BSD函数,但很容易复制实现)这将截断不适合目标缓冲区的字符串,但您确实需要评估字符串截断是否会导致自身的漏洞。我认为在测试源字符串是否合适时,更好的方法是进行新的分配或返回错误状态,而不是执行盲截断。

    如果您要进行大量的字符串连接/汇编,那么您真的应该编写所有代码来管理长度和当前位置。而不是:

    strcpy(out, str1);
    strcat(out, str2);
    strcat(out, str3);
    ...
    

    你应该这样做:

    size_t l, n = outsize;
    char *s = out;
    
    l = strlen(str1);
    if (l>=outsize) goto error;
    strcpy(s, str1);
    s += l;
    n -= l;
    
    l = strlen(str2);
    if (l>=outsize) goto error;
    strcpy(s, str2);
    s += l;
    n -= l;
    
    ...
    

    或者,可以通过保留当前索引来避免修改指针 i 类型 size_t 使用 out+i ,或者可以通过将指针保持在缓冲区的末尾并执行以下操作来避免使用大小变量 if (l>=end-s) goto error; .

    注意,无论您选择哪种方法,都可以通过编写自己的(简单的)函数来压缩冗余,这些函数将指针指向position/size变量并调用标准库,例如:

    if (!my_strcpy(&s, &n, str1)) goto error;
    

    避免 strcat 还具有性能优势;请参见 Schlemiel the Painter's algorithm .

    最后,您应该注意到,在C语言中,75%的字符串复制和程序集执行完全没有用处。我的理论是,做这件事的人来自脚本语言的背景,在脚本语言中,把字符串放在一起是你一直在做的事情,但在C语言中,这并不经常有用。在许多情况下,您可以完全不复制字符串,而是使用原始副本,同时获得更好的性能和更简单的代码。我想起了最近的一个问题,OP在哪里使用 regexec 要匹配正则表达式,然后复制结果以打印它,如下所示:

    char *tmp = malloc(match.end-match.start+1);
    memcpy(tmp, src+match.start, match.end-match.start);
    tmp[match.end-match.start] = 0;
    printf("%s\n", tmp);
    free(tmp);
    

    同样的事情也可以通过:

    printf("%.*s\m", match.end-match.start, src+match.start);
    

    没有分配,没有清理,没有错误案例(如果 malloc 失败)。