2

这是 C 语言的第三个程序。这个程序是为了演示字符串的概念。

从研究中我知道字符串末尾有一个空字符 '\0' 来终止字符串。

我写了那个代码:

main()
{
      char  name[8];

      strcpy(name, "Mahmoud");
      printf("The contents of name are %s\n", name);
      getchar();
}

在这段代码中,我声明了 char 类型的数组以在其中保存字符串。我的名字“Mahmoud”是 7 个字符,我声明名称的大小为 8,“Mahmoud”为 7,“\0”为 1,它可以正常工作。

但是在下面的代码中:

main()
{
      char  name[8];

      strcpy(name, "MahmoudEmam");
      printf("The contents of name are %s\n", name);
      getchar();
}

当我显示名称时,输出是“MahmoudEmam”,尽管名称的大小是 8。

它是怎么做的?

4

5 回答 5

5

C 不执行任何数组边界检查(可能使用 C11...)。您正在写超出数组的末尾:这是一种未定义的行为(任何事情都可能发生)。

于 2012-07-23T13:44:12.310 回答
4

您看到显示的全名的事实是未定义的行为。基本上,您将超过 7 个字符和一个终止 0(总共 8 个字符)复制到仅为 7 个字符和一个终止 0 保留的存储中。

它恰好可以工作,但很容易导致您的程序崩溃。

这就是为什么 C 被认为是一种较低级别的编程语言,或者,正如多年前所说的那样,一种高级汇编编程语言。

您的程序员必须检查复制操作目标的长度,使用类似的构造sizeof(name),并确保您正在复制的内容不会覆盖该空间。您还必须记住字符串终止,足够的空间 + 1 来说明终止的 '\0'。

不要忘记将 sizeof 与使用 malloc 分配的字符串一起使用将为 32 位指针或硬件的指针长度返回 4 值。在这种情况下,您将不得不依靠 strlen 来获取缓冲区长度,或者存储用于 malloc 字符串的大小。

最后,在将字符串指针传递给函数时,将函数编写为具有缓冲区长度非常有帮助。您无法获得真正的缓冲区长度。strlen 只返回字符串长度,而不是指针实际指向的缓冲区大小。

于 2012-07-23T13:48:54.377 回答
2

在这种情况下你很幸运。发生的事情是您在堆栈上为 7 个字母的字符串(+ 一个用于'\0')分配了足够的内存,但您正在写超出它的内容。

C 不会为您检测超出数组边界的读/写。

所以发生的事情是,你正在写数组的其余部分,破坏可能存在的任何其他内容。在较大的程序中,您很可能会遇到崩溃。

这是一个很好的例子,说明了为什么你应该学习使用strncpy,但请记住,如果缓冲区已满,strncpy则不会添加终止,你应该自己做。'\0'像这样的代码可以:

strncpy(name, "the text of your string, whatever it is", sizeof(name));
name[sizeof(name) - 1] = '\0';
于 2012-07-23T13:45:03.390 回答
1

您正在调用未定义的行为。你不能依赖它,因为它有效。

于 2012-07-23T13:44:27.000 回答
1

这个问题已经回答了,但如果可以的话,我想稍微改进一下。

如果您注意到,strcpy()不将缓冲区长度作为参数。那是因为它信任您,用户,注意不要溢出目标缓冲区。

C 中没有关于可以将哪些数据放置在内存中的哪个位置的限制。这同时是一种强大的工具,如果使用不当,也是一种危险的工具。

当您调用 时printf(),它会遍历您提供的缓冲区,直到找到第一个 0 并打印所有内容。如果您提供随机缓冲区,它将打印乱码。对于您编写的程序,您提供了一个缓冲区,其中包含您复制的字符串。即使您破坏了缓冲区,程序仍然设法找到并打印它,因为它甚至不知道内存已损坏。

缓冲区溢出是您能找到的最难的错误之一。它们通常会影响与导致损坏本身的模块无关的程序部分,并且通常需要很长时间才能感受到它们的影响。所以你应该非常小心,不要引起这样的错误!

于 2012-07-23T20:07:16.257 回答