MartinSebor译者
苏本如,责编
刘静以下为译文:在所有标准C语言string.h头文件中声明的字符串处理函数中,最常用的是那些用来复制和连接字符串的函数。这两组函数都将字符从一个对象复制到另一个对象,并且都返回它们的第一个参数:指向目标对象的起始指针。这种返回值的方式是导致函数效率低下的一个原因,而这正是本文要探讨的主题。本文中展示的示例代码仅仅用于说明目的。它们可能包含细微的错误,不应该被视为最佳代码实践。标准解决方案这种返回函数的第一个参数的设计,有时候会被不明白其用途的用户所质疑。这样的例子在StackOverflow网站上有不少,例如关于strcpy()返回值,或者C语言的strcpy为什么返回它的参数?的讨论。简单的答案是,这是一个历史性的意外。函数的第一个子集是由Unix第七版在年引入的,它由strcat、strncat、strcpy和strncpy函数组成。尽管这四个函数都在Unix的各种版本中使用,但通常情况下,对这些函数的调用却没有使用它们的返回值。尽管这些函数可以同样很容易地定义为返回一个指针来指向最后一个复制的字符(或它的后一位),而且事实证明这种做法也非常有用。两个或多个字符串的连接操作的最佳复杂度和字符数量成线性关系。但是,如上所述,让函数返回指向目标字符串的指针会导致操作的效率明显低于最佳效率。该函数遍历源字符串序列和目标字符串序列,并获取指向这两个序列末尾的指针。该指针指向函数(strncpy除外)附加到目标序列上的字符串结束符NUL(\0)处或它的后一位。但是,如果返回的指针指向第一个字符而不是最后一个字符(或它的下一个字符),NUL结束符的位置会丢失,必须在需要时重新计算。这种做法的低效率可以在将两个字符串s1和s2连接到目标缓冲区d中的示例中得到说明。将一个字符串添加到另一个字符串的惯用方法(虽然远非理想)是调用strcpy和strcat函数,如下所示:strcat(strcpy(d,s1),s2);为了执行这个连接操作,除了同时发生的相应地在d上的传递之外,一次在s1的传递和一次在s2上的传递是必须要执行的操作,但是上面的调用在s1上进行了两次传递。让我们把这些调用分成两个语句。char*d1=strcpy(d,s1);//pass1overs1strcat(d1,s2);//pass2overthecopyofs1ind因为strcpy返回其第一个参数d的值,所以d1的值与d相同。为简单起见,在后面的示例中我们将使用d,而不是将返回值存储在d1中并使用它。在strcat调用中,我们遍历刚刚复制到d1的字符串以确定最后一个字符的位置,这个成本和第一个字符串s1的长度是线性关系。这个成本乘以每个要连接的字符串。因而最终整个连接操作的成本相当于连接数和所以字符串长度的乘积,趋于一种二次方的关系。这种低效率是如此的臭名昭著,以至于为自己赢得了一个名字:画师施莱米尔算法。(另见