C和C ++如何在堆栈中存储大对象?

时间:2009-01-09 22:33:48

标签: c++ c memory stack

我试图弄清楚C和C ++如何在堆栈上存储大对象。通常,堆栈是整数的大小,所以我不明白在那里存储更大的对象。他们只是占用多个堆栈“插槽”吗?

11 个答案:

答案 0 :(得分:30)

答案 1 :(得分:26)

堆栈是一块内存。堆栈指针指向顶部。值可以在堆栈上推送并弹出以检索它们。

例如,如果我们有一个用两个参数调用的函数(1个字节大小,另外2个字节大小;假设我们有一个8位PC)。

两者都被推入堆栈,这会使堆栈指针向上移动:

03: par2 byte2
02: par2 byte1
01: par1

现在调用该函数并将返回的地址放在堆栈上:

05: ret byte2
04: ret byte1
03: par2 byte2
02: par2 byte1
01: par1

好的,在函数中我们有2个局部变量;两个字节中的一个和4个中的一个。对于这些位置是在堆栈上保留的,但首先我们保存堆栈指针,以便通过向上计数来知道变量的起始位置,并通过倒计时找到参数。

11: var2 byte4
10: var2 byte3
09: var2 byte2
08: var2 byte1
07: var1 byte2
06: var1 byte1
    ---------
05: ret byte2
04: ret byte1
03: par2 byte2
02: par2 byte1
01: par1

如您所见,只要剩余空间,您就可以在堆叠上放置任何东西。否则你会得到给这个网站起名字的现象。

答案 2 :(得分:9)

Pushpop指令通常不用于存储本地堆栈帧变量。在函数开始时,通过将堆栈指针递减函数的局部变量所需的字节数(与字大小对齐)来设置堆栈帧。这为这些值分配了“堆栈上”所需的空间量。然后通过指向此堆栈帧的指针(x86上的ebp)访问所有局部变量。

答案 3 :(得分:5)

堆栈是一个大的内存块,用于存储局部变量,从函数调用返回的信息等。堆栈的实际大小在操作系统上有很大差异。例如,在Windows上创建新线程时,default size is 1 MB

如果您尝试创建一个堆栈对象,该堆栈对象需要的内存大于堆栈上当前可用的内存,则会出现堆栈溢出并发生错误。一大类漏洞利用代码故意试图创建这些或类似的条件。

堆栈不会划分为整数大小的块。它只是一个平坦的字节数组。它由size_t(非int)类型的“整数”索引。如果您创建一个适合当前可用空间的大型堆栈对象,它只是通过向上(或向下)堆栈指针来使用该空间。

正如其他人所指出的,最好将堆用于大对象,而不是堆栈。这可以避免堆栈溢出问题。

编辑:如果您正在使用64位应用程序并且您的操作系统和运行时库对您很好(请参阅mrree的帖子),那么在上面分配大型临时对象应该没问题。堆。如果你的应用程序是32位和/或你的操作系统/运行时库不好,你可能需要在堆上分配这些对象。

答案 4 :(得分:3)

无论何时输入函数,堆栈都会增长以适应该函数中的局部变量。给定{400}字节的largeObject类:

void MyFunc(int p1, largeObject p2, largeObject *p3)
{
   int s1;
   largeObject s2;
   largeObject *s3;
}

调用此函数时,您的堆栈将如下所示(详细信息将根据调用约定和体系结构而有所不同):

   [... rest of stack ...]
   [4 bytes for p1] 
   [400 bytes for p2]
   [4 bytes for p3]
   [return address]
   [old frame pointer]
   [4 bytes for s1]
   [400 bytes for s2]
   [4 bytes for s3]

有关堆栈如何运作的一些信息,请参阅x86 Calling Conventions。 通过Sample Coderesulting stack diagrams,MSDN还为一些不同的呼叫对流提供了一些很好的图表。

答案 5 :(得分:2)

正如其他人所说的,目前还不清楚“大型物品”的含义......但是,既然你要问

  

他们只是占用多个筹码   “槽”?

我要假设你只是指大于整数的东西。然而,正如其他人所指出的那样,堆栈没有整数大小的“槽” - 它只是内存的一部分,并且其中的每个字节都有自己的地址。编译器通过该变量的 first 字节的地址跟踪每个变量 - 这是您使用address-of运算符(&var)时获得的值,以及值指针只是其他变量的地址。编译器也知道每个变量的类型(你在声明变量时告诉它)​​,并且它知道每个类型应该有多大 - 当你编译程序时,它会做任何数学运算来计算这些空间的大小。调用函数时需要变量,并在函数入口点代码(PDaddy提到的堆栈框架)中包含该结果。

答案 6 :(得分:1)

在C和C ++中,您不应该在堆栈上存储大对象,因为堆栈是有限的(正如您猜测的那样)。每个线程的堆栈通常只有几兆字节或更少(可以在创建线程时指定)。当你调用“new”创建一个对象时,它不会被放在堆栈上 - 而是放在堆上。

答案 7 :(得分:1)

堆栈大小有限。通常在创建进程时设置堆栈大小。如果未在CreateThread()调用中另行指定,则该进程中的每个线程将自动获取默认堆栈大小。所以,是的:可以有多个堆栈'插槽',但每个线程只有一个。它们不能在线程之间共享。

如果将大于剩余堆栈大小的对象放入堆栈,则会出现堆栈溢出,并且应用程序将崩溃。

因此,如果您有非常大的对象,请在堆上分配它们,而不是在堆栈上。堆只受虚拟内存量的限制(比堆栈大一些)。

答案 8 :(得分:0)

“stack是一个整数的大小”,你的意思是“堆栈指针是一个整数的大小”。它指向堆栈的顶部,这是一个巨大的内存区域。好吧,大于整数。

答案 9 :(得分:0)

你可以拥有足够大(或足够多)的对象,将它们放在堆栈上是没有意义的。在这种情况下,您可以将对象放在堆上并在堆栈上放置指向它的指针。这是传递值和传递参考之间的差异。

答案 10 :(得分:0)

如何定义大对象?我们说的是大于还是小于分配的堆栈空间的大小?

例如,如果你有这样的东西:

void main() {
    int reallyreallybigobjectonthestack[1000000000];
}

根据您的系统,您可能会遇到段错误,因为没有足够的空间来存储对象。否则它会像任何其他对象一样存储。如果您在实际的物理内存中进行通话,那么您不必担心这一点,因为操作系统级别的虚拟内存将负责处理。

此外,堆栈的大小可能不是整数的大小,而是完全取决于您的操作系统和应用程序的布局Virtual Address Space