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

时间:2010-11-15 15:04:40

标签: c string

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

例如,为了进行字符串复制,我可以使用:

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

虽然我知道所有这些替代方案都有历史原因,但我可以为新代码选择一组一致的函数吗?哪一个?或者我应该根据具体情况选择最合适的功能?

6 个答案:

答案 0 :(得分:13)

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

ANSI C标准库函数(CRT)

如果您正在开发可移植的C代码,那么像strcpy这样的函数是唯一的选择。即使在仅限Windows的项目中,将便携式与操作系统相关的代码分离也许是明智之举 这些功能通常具有装配级别优化,因此非常快 有一些缺点:

  • 它们有很多限制,因此通常您仍需要调用其他库中的函数或提供自己的版本
  • 有一些像臭名昭着的strncpy
  • 这样的古体

Kernel32字符串函数

lstrcpy这样的函数由kernel32导出,只有在尝试避免对CRT的依赖时才应使用。您可能希望这样做有两个原因:

  • 避免超轻量级可执行文件的CRT有效负载(这些天不寻常,但不是10年前!)
  • 避免初始化问题(如果您使用CreateThread而不是_beginthread启动帖子。)

此外,kernel32函数可以更优化CRT版本:当您的可执行文件将在针对Core i13优化的Windows 9上运行时,kernel32 可以使用程序集 - 优化版。

Shell Lightweight Utility Functions

这对于kernel32函数的相同考虑是有效的,具有一些更复杂函数的附加值。但是我怀疑他们是否得到积极维护,我会跳过它们。

StrSafe功能

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

安全性增强的CRT功能

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

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

结论

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

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

答案 1 :(得分:4)

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

答案 2 :(得分:3)

我会回答这个问题略有不同。你想要便携式代码吗?如果您想要可移植,除了strcpystrncpy或标准宽字符“字符串”处理函数之外,您不能依赖任何其他内容。

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

如果您想要便携并且仍然希望有一些额外的安全性,那么您应该检查跨平台库,例如  gliblibapr 或其他“安全字符串库”,例如: SafeStrLibrary

答案 3 :(得分:1)

我建议使用标准库中的函数,或跨平台库中的函数。

答案 4 :(得分:0)

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

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

答案 5 :(得分:0)

在这些选择中,我只会使用strcpy。至少strcpy_slstrcpy是绝不应该使用的。调查那些独立编写的库函数可能是值得的,但我会犹豫是否将非标准库代码作为字符串安全的灵丹妙药。

如果您正在使用strcpy,则需要确保您的字符串适合目标缓冲区。如果您刚刚分配了大小至少为strlen(source)+1的大小,只要源字符串不会同时受到另一个线程的修改,您就可以了。否则,您需要测试它是否适合缓冲区。您可以使用snprintfstrlcpy等接口(非标准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;

之类的操作

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

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

避免strcat也具有性能优势;见Schlemiel the Painter's algorithm

最后,你应该注意到,75%的字符串复制和装配人员在C中表现完全没用。我的理论是,这样做的人来自脚本语言的背景,在这里,你总是把字符串放在一起,但在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失败,原始代码崩溃了。)