1

这是我写的代码

    char *foo();

    void main()
    {
      char *str=foo();
      strcpy(str,"Holy sweet moses! I blew my stack!!");
      printf("%s",str);
    }

    char * foo()
    {
      char str[256];
      return str;
    }

当我在函数 foo() 中使用 char 数组时,main() 函数中的 strcpy 不会将字符串复制到 str 中。但是,当我在函数 foo() 中使用 int 数组时,main() strcpy 复制成功。

IE

   int str[256]; //in function foo

输出

   Holy sweet moses! I blew my stack!!

如果

   char str[256]; //in foo()

输出:什么都没有!

4

4 回答 4

5

您所做的显然是 UNDEF,但是.. 让我们尝试了解为什么它适用于 int 而不是 chars..

TL;DR:printf 使用堆栈,覆盖 str 指向的一些空间,但由于 int 数组在内存中比 char 数组大,因此它在堆栈中“遥遥领先”并且不会被覆盖。

一个 int 是 4 个字节,所以 256 个 int 将是 1024 个字节。

如果数组在堆栈中,这将指向 RBP - 例如 1024。

对于字符,一个字符是 1 个字节,256 个字符是 256 个字节。

如果数组在堆栈中,这将指向 RBP - 例如 256。

这是什么意思?当 foo 返回时,str 指针将指向当前堆栈指针“提前”的 1024 或 256 个字节。

所以 .. 当你调用 strcpy(str, "yourstring"); 该内存可能会被 strcpy 和 printf 使用的堆栈覆盖。这里的问题是它被覆盖但不是所有堆栈,只是一点点,但足以覆盖 256 个字节,因此,该函数可以覆盖复制的字符串,这不会发生在您的 int 数组中,因为字符串会在堆栈指针之前复制 1024 位,并且 strcpy 和 printf 不使用太多堆栈。

让我向您展示您的堆栈将如何结束:

在此处输入图像描述

如果您更改 char 数组的大小,它可能会起作用。

所有这些都是未定义的行为,完全取决于您的架构、计算机和编译器。我目前正在使用 Linux x86_64。

于 2013-09-19T07:31:37.190 回答
2

您知道变量的范围和生命周期概念吗?如果是,那么您确实知道您正在尝试执行的操作会调用"Undefined Behavior"。您很幸运,您的代码甚至打印出某些内容或根本不打印,而不是引用未分配的内存并由于堆损坏而崩溃。

来自SO Soln ::

操作系统或语言运行时在多大程度上控制堆栈/堆?

操作系统在创建线程时为每个系统级线程分配堆栈。通常,语言运行时调用操作系统来为应用程序分配堆。

他们的范围是什么?

堆栈附加到线程,因此当线程退出时,堆栈被回收。堆通常由运行时在应用程序启动时分配,并在应用程序(技术进程)退出时回收。

是什么决定了它们每个的大小?

堆栈的大小在创建线程时设置。堆的大小在应用程序启动时设置,但可以随着空间的需要而增长(分配器从操作系统请求更多内存)。

于 2013-09-19T07:04:12.513 回答
0

当您的函数foo()返回时,它会从堆栈中返回字符串的地址。当函数退出时,您的指针是无用的,因为当 foo() 停止时,您的字符串会从堆栈中删除。所以你有一个指向内存中某个地方的指针,但不知道那里有什么

阅读编译器警告,我敢打赌至少有一个告诉你你的函数返回指向局部变量的指针。(我的做了,我做了几次次)。

于 2013-09-19T07:05:11.993 回答
0

从你关于炸毁堆栈的字符串中,我得出结论,你知道你在做什么是错误的。因此我的回答是:未定义的行为是未定义的。写入不属于您的内存时,可能会发生任何事情,包括您可能期望的事情和您可能不期望的事情。探索其他未定义的行为可能有点有趣,写入不属于您的内存则不是。它总是错误的,它总是会做一些你意想不到的事情,而且没有任何情况下正确的解决方案是不再做。

当您将数组从 char 更改为 int 时,您会更改数组的大小,并且由于堆栈很可能在您的体系结构上增长,它会更改您不应该首先覆盖的内存的地址。

于 2013-09-19T07:33:28.327 回答