strcpy和stpcpy之间有什么区别?

25

阅读strcpy函数的手册时,我发现还存在一个stpcpy函数。然而,在手册中我注意到的唯一区别是:

char *
stpcpy(char *s1, const char *s2);

char *
strcpy(char *restrict s1, const char *restrict s2);

那么,这里的restrict是什么意思?


@{大家}:一如既往,我收到了许多优秀的答案,但无法将它们全部标记为正确。在这些问题中,决定哪个答案是最难的部分。我尝试考虑问题所涉及的内容以及有时候它们的年龄;然而,许多不同的答案提供了关于不同主题和不同层次的重要信息。感谢大家,但很抱歉,我除了点赞之外不能做更多的事情。 - sidyll
6个回答

35
你在标题中提出的问题和关于 "restrict" 的问题实际上是两个完全不相关的问题。其他答案已经为你提供了一些有用的链接,可以帮助你更多地了解 "restrict"。
然而,这两个函数之间的主要区别并不在于 "restrict" 限定符。事实上,在 C 语言规范的 C99 版本中,strcpy 函数的参数也具有 restrict 限定符。你在 man 手册中看到的 strcpy 描述只是未更新以符合C99标准。
主要区别(你可能忽略了)在于 stpcpy 的返回值。stpcpy 返回指向目标字符串终止字符 '\0' 的指针。这立即说明了 stpcpy 的目的及存在的原因:当你需要将多个子字符串连接成一个字符串时,该功能旨在作为 strcat 函数的智能替代品使用。你看,strcat 在这种应用中效果相当糟糕(我甚至会说 strcat 在实际代码中没有任何有意义的用途)。strcat 的问题在于每次添加内容时都会重新扫描目标字符串,从而做了许多不必要的工作,并且基本上只是制造了更多的麻烦而已。例如,以下代码就受到了这个问题的影响:
const char *part1, *part2, *part3, *part4;
...
char buffer[size]; /* assume that `size` is calculated properly */

strcpy(buffer, part1);
strcat(buffer, part2);
strcat(buffer, part3);
strcat(buffer, part4);

通过使用stpcpy,可以以更加合理的方式重新实现此代码。

stpcpy(stpcpy(stpcpy(stpcpy(buffer, part1), part2), part3), part4);

如果你不喜欢链式调用,你可以使用一个中间指针来存储中间 stpcpy 调用的返回值。

char *end = buffer;

end = stpcpy(end, part1);
end = stpcpy(end, part2);
end = stpcpy(end, part3);
end = stpcpy(end, part4);

当然值得一提的是,strcpystrcat是标准函数,而stpcpy不是。


很遗憾我不能给两个答案打上正确的标记,所以最终我只能点赞并感谢你提供这些优秀的示例。 - sidyll
我认为这是实际上对问题的回答,至少是标题中的问题。 - Seanoseanohay
2
值得一提的是,stpcpy自2008年版本起已在POSIX中定义。 - graywolf
1
我甚至可以说,在实际代码中,strcat 没有任何有意义的用途。strcat 的独特糟糕设计使其成为 Joel on Software 博客文章中引入“Shlemiel the painter's algorithm”术语的典型例子 - ShadowRanger
除了您的回答外,我想知道如何深入理解C函数?是否有一本书可以阅读,因为man页面并没有说明函数实际在后台执行什么。 - Lady Be Good

21
restrict关键字告诉编译器,s1s2指向不同的数组,并且指向的数组没有重叠。在某些情况下,这可能允许编译器执行额外的优化(例如,它可能可以复制多个字符块而无需检查重叠)。
请注意返回值也有所不同:stpcpy返回指向被复制到目标缓冲区中的\0的指针,而strcpy返回指向字符串开头的指针(实际上它执行了return s1;)。

1
stpcpy是POSIX标准的一部分:http://pubs.opengroup.org/onlinepubs/9699919799/functions/stpcpy.html。 - R.. GitHub STOP HELPING ICE
1
公平地说,它只是在 POSIX 2008 中添加的。 - R.. GitHub STOP HELPING ICE

7

非常感谢你的链接,马克。令人印象深刻的是,我之前并不知道这个关键词。 - sidyll

4

stpcpy()函数将src指向的字符串(包括终止的'\0'字符)复制到dest指向的数组中。这两个字符串不能重叠,目标字符串dest必须足够大以接收复制内容。

即使在函数签名中没有使用restrict,也满足其要求。如果有C99,则可以添加。

stpcpy()函数返回指向字符串dest结尾(即终止空字节的地址)的指针,而不是开头。

这使您更轻松、更有效地连接多个字符串,因为可以将src“附加”到上一个stpcpy()返回的指针上。而替代方法strcat的朴素实现必须在开始复制之前找到dest字符串的结尾。

请注意以下内容:

此函数不属于C或POSIX.1标准,也不是Unix系统的惯例,但也不是GNU发明。也许它来自MS-DOS。现在,在BSD上也存在该函数。


感谢你的全面回答,Matt。我对 stpcpy 不是 C(或 POSIX)的一部分有点困惑,因为我看到的 man 页面除了包含 string.h 之外没有其他要求。但是你提到的 BSD 让我明白了。我使用的是一个被广泛称为 Mac OS X 的 FreeBSD 版本。 - sidyll
@sidyll:你选择 Mac OS X 是一个非常好的决定,所以干得好 :) - Matt Joiner

3
另一个不同之处在于返回值。从手册页中可以看到:"strcpy()和strncpy()函数返回s1。stpcpy()函数返回指向s1终止的'\0'字符的指针。"

3

strcpy函数返回s1,而stpcpy函数返回s1+strlen(s2)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接