在我的Windows/Visual C环境中,有很多替代方法可以完成相同的基本字符串操作任务。
例如,对于字符串复制,我可以用途:
strcpy
,ANSI C标准库函数(CRT)lstrcpy
,包含在kernel32.dll中的版本StrCpy
,来自Shell轻量级实用程序库StringCchCopy
/StringCbCopy
,来自“安全字符串”库strcpy_s
,CRT的安全增强版
虽然我知道所有这些选择都有历史原因,但我可以为新代码选择一组一致的函数吗?选择哪一个?还是应该根据具体情况选择最合适的函数?
6条答案
按热度按时间kq0g1dla1#
首先,让我们回顾一下每个功能集的优缺点:
ANSI C标准库函数(CRT)
如果你在开发可移植的C代码,像
strcpy
这样的函数是你唯一的选择。即使在一个纯Windows的项目中,把可移植的代码和依赖于操作系统的代码分开也是一件明智的事情。这些函数通常具有汇编级优化,因此非常快。
有一些缺点:
strncpy
Kernel 32字符串函数
像
lstrcpy
这样的函数是由kernel32
导出的,只有在试图避免对CRT的任何依赖时才应该使用。CreateThread
而不是_beginthread
启动线程)。此外,
kernel32
函数 * 可以 * 比CRT版本更加优化:当您的可执行文件将在针对酷睿i13优化的Windows 12上运行时,kernel32
* 可以 * 使用程序集优化版本。Shell轻量级实用程序函数
下面是对
kernel32
函数的同样考虑,加上一些更复杂的函数的附加值。然而,我怀疑它们是否得到了积极的维护,我将直接跳过它们。StrSafe函数
StringCchCopy
/StringCbCopy
函数通常是我个人的选择:它们设计得非常好,功能强大,速度惊人(我还记得一篇白皮书,将这些功能的性能与CRT等效功能进行了比较)。安全增强型CRT功能
这些函数无疑有着与ANSI C等价物非常相似的优点,因此移植遗留代码是小菜一碟。我特别喜欢基于模板的版本(当然,只有在编译为C++时才可用)。我真的希望它们最终会被标准化。不幸的是,它们有许多缺点:
结论
虽然我个人最喜欢的Windows开发是StrSafe库,但我的建议是尽可能使用ANSI C函数,因为可移植代码总是一件好事。
在真实的生活中,我开发了一个个性化的可移植库,原型类似于安全增强的CRT函数(包括强大的基于模板的技术),它依赖于Windows上的StrSafe库和其他平台上的ANSI C函数。
mec1mxoz2#
对于新的和现有的项目,我个人的偏好是safe字符串库中的
StringCchCopy/StringCbCopy
版本,我发现这些函数总体上非常一致和灵活,而且它们是从安全性/保密性的Angular 设计的。d7v8vwbk3#
我的回答略有不同。你想拥有可移植的代码吗?如果你想拥有可移植的代码,除了
strcpy
,strncpy
,或者标准的宽字符“string”处理函数之外,你不能依赖其他任何东西。然后,如果您的代码必须在Windows下运行,则可以使用“安全字符串”变体。
如果你想要便携并且仍然想要一些额外的安全性,那么你应该检查跨平台库,例如glib或libapr或其他“安全字符串库”,例如:SafeStrLibrary
uidvcgyl4#
我建议使用标准库中的函数,或者使用跨平台库中的函数。
cgfeq70w5#
我会坚持一个,我会选择任何一个是在最有用的库,以防您需要使用更多的它,我会远离kernel32.dll之一,因为它只是窗口。
但这些只是提示,这是一个主观问题。
lsmepo6l6#
在这些选择中,我会简单地使用
strcpy
。至少strcpy_s
和lstrcpy
是不应该使用的。研究那些独立编写的库函数可能是值得的,但我会犹豫是否要将非标准库代码作为字符串安全的灵丹妙药。如果你使用
strcpy
,你需要确保你的字符串适合目标缓冲区。如果你刚刚给它分配了至少strlen(source)+1
的大小,只要源字符串不同时被另一个线程修改就可以了。否则你需要测试它是否适合缓冲区。你可以使用snprintf
或strlcpy
这样的接口(非标准BSD函数,但易于复制实现),它将截断不适合目标缓冲区的字符串,但是您确实需要评估字符串截断是否会导致其自身的漏洞。我认为测试源字符串是否合适的更好方法是进行新的分配或返回错误状态,而不是执行盲目截断。如果你要做大量的字符串连接/汇编,你真的应该编写所有的代码来管理长度和当前位置,而不是:
你应该这样做:
或者,您可以通过保留
size_t
类型的当前索引i
并使用out+i
来避免修改指针,或者您可以通过保留指向缓冲区末尾的指针并执行类似if (l>=end-s) goto error;
的操作来避免使用大小变量。请注意,无论您选择哪种方法,都可以通过编写自己的(简单的)函数来压缩冗余,这些函数获取指向位置/大小变量的指针并调用标准库,例如:
避免
strcat
也有性能优势;参见Schlemiel the Painter's algorithm。最后,你应该注意到,在C语言中,有75%的字符串复制和汇编是完全无用的。我的理论是,做这件事的人来自脚本语言的背景,在那里,把字符串放在一起是你一直在做的事情,但在C语言中,这并不经常有用。在许多情况下,你可以完全不复制字符串,而是使用原始副本。同时获得更好的性能和更简单的代码。我想起了最近的一个SO问题,其中OP使用
regexec
匹配正则表达式,然后复制结果以打印它,类似于:同样的事情可以通过以下方式实现:
没有分配,没有清理,没有错误情况(如果
malloc
失败,原始代码崩溃)。