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

C 我应该使用哪些字符串操作函数?,c,string,C,String,在我的Windows/VisualC环境中,有许多替代方法可以执行相同的基本字符串操作任务 例如,对于进行字符串复制,我可以使用: strcpy,ANSI C标准库函数(CRT) lstrcpy,包含在kernel32.dll中的版本 StrCpy,来自Shell轻量级实用程序库 StringCchCopy/StringCbCopy,来自“安全字符串”库 strcpy\u s,CRT的安全增强版 虽然我知道所有这些替代方案都有历史原因,但我能为新代码选择一组一致的函数吗?哪一个呢?或者我应该

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

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

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

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

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


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

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

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

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

如果您使用的是strcpy,则需要确保字符串适合目标缓冲区。如果您只为它分配了至少
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;

...
if (!my_strcpy(&s, &n, str1)) goto error;
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);
或者,您可以通过保留类型为
size\u t
的当前索引
i
并使用
out+i
来避免修改指针,或者您可以通过将指针保持在缓冲区的末尾并执行类似于
if(l>=end-s)goto error的操作来避免使用大小变量

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

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;

...
if (!my_strcpy(&s, &n, str1)) goto error;
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);
避免使用strcat也有性能优势;看

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

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;

...
if (!my_strcpy(&s, &n, str1)) goto error;
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
失败,原始代码就会崩溃)。

我的回答会稍微不同。您是否想要可移植代码?如果您想成为可移植的,您只能依赖于标准的宽字符“字符串”处理函数

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

如果你想成为一个可移植的并且仍然想有一些额外的安全性,那么你应该检查跨平台库,例如 或 或其他“安全字符串库”,例如:

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

ANSI C标准库函数(CRT) 如果您正在开发可移植的C代码,像strcpy这样的函数是唯一的选择。即使是在仅限Windows的项目中,将可移植代码与依赖操作系统的代码分开可能是一件明智的事情。
这些功能通常具有组件级优化,因此速度非常快。
有一些缺点:

  • 它们有很多限制,因此通常您仍然需要从其他库调用函数或提供自己的版本
  • 有一些古语,比如臭名昭著的
    strncpy
内核32字符串函数 像
lstrcpy
这样的函数是由kernel32导出的,只有在试图避免依赖于CRT时才应该使用。您可能希望这样做有两个原因:

  • 为超轻量可执行文件避免CRT有效负载(这在今天很少见,但不是10年前!)
  • 避免初始化问题(如果使用
    CreateThread
    而不是
    \u beginthread
    启动线程)
莫尔