10

以下是传统系统中最流行的 strcpy 实现。为什么在开始时不检查 dest 和 src 是否为 NULL?我曾经听说在过去内存有限,所以总是首选短代码。您会在开始时使用 NULL 指针检查来实现 strcpy 和其他类似功能吗?为什么不?

char *strcpy(char *dest, const char *src)
{
   char *save = dest;
   while(*dest++ = *src++);
   return save;
}
4

9 回答 9

20

NULL是一个坏指针,但(char*)0x1. 它也应该检查吗?在我看来(我不知道确切的原因),在如此低级别的操作中进行健全性检查是不必要的。strcpy()它是如此基础,以至于它应该被视为 asm 指令,如果需要,您应该在调用者中进行自己的健全性检查。只是我的 2 美分 :)

于 2010-09-01T08:45:33.743 回答
15

没有健全性检查,因为 C 最重要的潜在意识形态之一是开发人员提供健全性。当您假设开发人员是理智的,您最终会得到一种可以用来在任何地方做任何事情的语言。

这不是一个明确声明的目标——有人很可能想出一个可以检查这一点的实现等等。也许他们有。但我怀疑许多习惯于 C 的人会叫嚣使用它,因为如果他们的代码有可能被移植到更常用的实现中,他们无论如何都需要进行检查。

于 2010-09-01T09:01:06.763 回答
11

整个 C 语言的座右铭是“只要程序员知道他在做什么,我们就会表现得正确”。程序员应该知道做他需要做的所有检查。它不只是检查 NULL,它确保dest指向足够分配src的内存来保存,它检查返回值fopen以确保文件确实成功打开,知道什么时候memcpy是安全的,什么时候memmove需要,等等。

检查NULLstrcpy不会改变语言范式。您仍然需要确保dest指向足够的空间——这是在不更改界面的情况下strcpy 无法检查的内容。您还需要确保它src'\0'-terminated,这又strcpy不可能检查。

有一些 C 标准库函数检查 NULL:例如,free(NULL)总是安全的。但总的来说,C 希望你知道你在做什么。

[C++ 通常会避开<cstring>库以支持std::string和朋友。]

于 2010-09-01T08:59:20.040 回答
6
  1. 库通常最好让调用者决定它想要的失败语义是什么。strcpy如果任何一个论点都是,你会怎么做NULL?默默无闻?失败assert(在非调试版本中不是一个选项)?

  2. 选择加入比选择退出更容易。编写自己的包装器strcpy来验证输入并改用它是微不足道的。但是,如果库自己执行此操作,您将无法选择不执行这些检查,除非重新实现strcpy. (例如,您可能已经知道传递给的参数 is strcpynot NULL,如果您在紧密循环中调用它或担心最小化功耗,这可能是您关心的事情。)一般来说,它更好在给予更多自由方面犯错(即使这种自由伴随着额外的责任)。

于 2010-09-01T10:45:44.953 回答
3

最可能的原因是:因为strcpy未指定使用NULL输入(即在这种情况下其行为未定义)。

NULL那么,如果传入a ,库实现者应该选择做什么呢?我认为最好的办法是让应用程序崩溃。可以这样想:崩溃是出现问题的一个相当明显的迹象……NULL另一方面,默默地忽略输入可能会掩盖一个更难检测的错误。

于 2010-09-01T08:46:41.830 回答
2

NULL 检查没有实现,因为 C 的最早目标支持强大的内存保护。当一个进程试图读取或写入 NULL 时,内存控制器将向 CPU 发出信号,表明尝试了超出范围的内存访问(分段违规),并且内核将终止有问题的进程。

这是一个不错的答案,因为尝试读取或写入 NULL 指针的代码被破坏了;唯一的答案是重新编写代码以检查来自malloc(3)和朋友的返回值并采取纠正措施。当您尝试使用指向未分配内存的指针时,要就如何解决这种情况做出正确的决定已经太迟了。

于 2010-09-01T08:47:01.643 回答
0

根本没有为它定义错误语义。特别是没有办法strcpy返回错误值。C99 简单地说:

strcpy函数返回 的值s1

因此,对于符合要求的实现,甚至不可能返回出现问题的信息。那何必费心呢。

我认为所有这些都是自愿的,因为strcpy大多数编译器直接被非常高效的汇编器取代。错误检查由调用者决定。

于 2010-09-01T11:18:42.080 回答
0

您应该将 C 标准库函数视为汇编代码之上的最薄的附加抽象层,您不希望大量生产这些抽象层来获取您的东西。除此之外的一切,比如错误检查,都是你的责任。

于 2010-09-01T09:05:31.760 回答
0

根据我的说法,您想要定义的任何函数都将具有前置条件和后置条件。处理前提条件永远不应该成为函数的一部分。以下是使用从手册页获取的 strcpy 的先决条件。

strcpy() 函数将 src 指向的字符串(包括终止字符 '\0' )复制到 dest 指向的数组中。字符串不能重叠,并且目标字符串 dest 必须足够大以接收副本。

现在,如果不满足前提条件,那么事情可能是不确定的。

我现在是否会在我的 strcpy 中包含一个 NULL 检查。我宁愿有另一个 safe_strcpy,把安全放在首位,我肯定会包括 NULL 检查和处理溢出条件。因此,我的先决条件得到了修改。

于 2010-09-01T09:13:48.587 回答