就目前而言,在编程领域中,C语言的运用非常之多,它兼顾了高级语言的汇编语言的优点,相较于其它编程语言具有较大优势 。
文章插图
作者 | Martin Sebor
译者 | 苏本如,责编 | 刘静
出品 | CSDN(ID:CSDNnews)
以下为译文:
在所有标准C语言<string.h>头文件中声明的字符串处理函数中,最常用的是那些用来复制和连接字符串的函数 。这两组函数都将字符从一个对象复制到另一个对象,并且都返回它们的第一个参数:指向目标对象的起始指针 。这种返回值的方式是导致函数效率低下的一个原因,而这正是本文要探讨的主题 。
本文中展示的示例代码仅仅用于说明目的 。它们可能包含细微的错误,不应该被视为最佳代码实践 。
1.标准解决方案这种返回函数的第一个参数的设计,有时候会被不明白其用途的用户所质疑 。这样的例子在StackOverflow网站上有不少,例如关于strcpy返回值,或者C语言的strcpy为什么返回它的参数?的讨论 。简单的答案是,这是一个历史性的意外 。函数的第一个子集是由Unix第七版在1979年引入的,它由strcat、strncat、strcpy和strncpy函数组成 。尽管这四个函数都在Unix的各种版本中使用,但通常情况下,对这些函数的调用却没有使用它们的返回值 。尽管这些函数可以同样很容易地定义为返回一个指针来指向最后一个复制的字符(或它的后一位),而且事实证明这种做法也非常有用 。
两个或多个字符串的连接操作的最佳复杂度和字符数量成线性关系 。但是,如上所述,让函数返回指向目标字符串的指针会导致操作的效率明显低于最佳效率 。该函数遍历源字符串序列和目标字符串序列,并获取指向这两个序列末尾的指针 。该指针指向函数(strncpy除外)附加到目标序列上的字符串结束符NUL('')处或它的后一位 。但是,如果返回的指针指向第一个字符而不是最后一个字符(或它的下一个字符),NUL结束符的位置会丢失,必须在需要时重新计算 。这种做法的低效率可以在将两个字符串s1和s2连接到目标缓冲区d中的示例中得到说明 。将一个字符串添加到另一个字符串的惯用方法(虽然远非理想)是调用strcpy和strcat函数,如下所示:
strcat (strcpy (d, s1), s2);
为了执行这个连接操作,除了同时发生的相应地在d上的传递之外,一次在s1的传递和一次在s2上的传递是必须要执行的操作,但是上面的调用在s1上进行了两次传递 。让我们把这些调用分成两个语句 。char *d1 = strcpy (d, s1); // pass 1 over s1
strcat (d1, s2); // pass 2 over the copy of s1 in d
因为strcpy返回其第一个参数d的值,所以d1的值与d相同 。为简单起见,在后面的示例中我们将使用d,而不是将返回值存储在d1中并使用它 。在strcat调用中,我们遍历刚刚复制到d1的字符串以确定最后一个字符的位置,这个成本和第一个字符串s1的长度是线性关系 。这个成本乘以每个要连接的字符串 。因而最终整个连接操作的成本相当于连接数和所以字符串长度的乘积,趋于一种二次方的关系 。这种低效率是如此的臭名昭著,以至于为自己赢得了一个名字:画师施莱米尔算法 。(另见http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2349.htm#sad-string)
必须指出的是,除了效率低下之外,strcat和strcpy还因其缓冲区溢出的问题而臭名昭著,因为它们都对复制字符的数量不做任何限制 。
2.克服局限性的尝试当源字符串的长度未知且目标字符串大小固定时,遵循一些流行的安全编码准则来将连接结果限制为目标区大小实际上会导致两个冗余的传递 。例如,按照CERT关于安全使用strncpy和strncat 的建议,并且目标区的大小是dsize字节,我们可能会得到以下代码 。
strncpy (d, s1, dsize - 1); // pass 1 over s1 plus over d up to dsize - 1d[dsize - 1] = ''; // remember to nul-terminatesize_t n = strlen (d); // pass 2 over copy of s1 in dstrncat (d, s2, dsize - n - 1); // pass 3 over copy of s1 in d
注意,与对strncat的调用不同,当s1的长度大于d的大小时,上面对strncpy的调用不会将NUL('')结束符追加到d上 。它是一个常见的想当然的错误 。此外,当s1短于dsize-1时,strncpy函数将所有剩余的字符填满为NUL(''),这也被视为一种浪费的,因为随后对strncat的调用将覆盖掉它们 。为了避免一些冗余,程序员有时会选择先计算字符串长度,然后使用memcpy,如下所示 。这种方法仍然效率不高,而且更容易出错,并且代码难以阅读和维护 。
推荐阅读
- ”什么是内网穿透“详解
- 「C语言」常用算法
- 现在开淘宝网店是不是一定要交保证金 开淘宝店必须交保证金吗
- 总结Java中return语句的用法
- Mac平台上实现视频采集
- CentOS 8 安装图解
- 白鹿茶韵词语茶香与茶友起体验传统文化的魅力
- 中欧国际茶座交易中心在新疆阿拉山口开设
- 虾球烩豆腐
- 关于茶或促进糖尿病伤口愈合的研究表明茶多酚在其中发挥作用