代码之家  ›  专栏  ›  技术社区  ›  Nate879

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

  •  18
  • Nate879  · 技术社区  · 16 年前

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

    11 回复  |  直到 16 年前
        1
  •  35
  •   Community CDub    8 年前

    堆栈和堆并不像你想象的那么不同!


    诚然,有些操作系统确实存在堆栈限制。(其中一些也有令人讨厌的堆限制!)

    但现在已经不是1985年了。

    我的默认 堆栈大小 限制为10 MB。我的默认 是无限的。取消对堆栈大小的限制非常简单。(*咳嗽*[tcsh] 未限制堆栈大小 setrlimit() .)

    1. 堆栈
    2. 堆栈 当前块结束时超出范围。 调用delete/free时超出范围。
    3. 堆栈 永远不会支离破碎。

    在Linux下,两者 堆栈 通过虚拟内存进行管理。

    就分配时间而言,即使在碎片严重的内存中进行堆搜索,也无法在内存的新页面中进行映射。 时间上的差异可以忽略不计!

    malloc() 分配!)(这是一本书 惰性评估

    ( 刚出现的 将调用构造函数,该构造函数可能会使用这些内存页…)


    堆栈 或者 malloc()

    虽然被调出的页面需要被调回,这将是你最受欢迎的时间。


    在所有这些事情中, !

    寿命(超出范围时)始终是决定因素。




    修订如下:


    老兄,我被宠坏了!

    这里有些东西不对劲。。。我想,要么,要么,我,离基地太远了。或者其他人都是。或者,更有可能两者兼而有之。或者,只是可能,两者都不是。

    不管答案是什么,我必须知道到底发生了什么!

    …这会很长的。请容忍我。。。


    我在Windows上做了一些工作,但还不够权威。不幸的是,Mac OS/Darwin也没有。。。尽管Mac OS/Darwin/BSD已经足够接近了,我的一些知识还是流传了下来。


    使用32位指针时,4GB(2^32)的地址空间会用完。

    堆栈 + 合并的是 usually limited to somewhere between 2-4 GB as other things need to get mapped in there.


    在Linux/Unix/MacOS/Darwin/BSD下,您可以人为地约束

    这是(在tcsh中)的区别 “限制-h” . 或(在狂欢中)的 “ulimit-Sa” vs . 或者,以编程方式 里姆库尔 马克斯 结构限制 .


    现在我们进入有趣的部分。关于 马丁约克密码 马丁 ! 很好的例子。尝试总是好的!)

    马丁的

    当然,默认情况下,他的代码不会在Mac上运行。但如果他先调用 “未限制堆栈大小” (tcsh)或 (猛击)。


    问题的核心是:


    在一个古老(过时)的Linux RH9 2.4.x内核盒上测试,分配大量 ,任何一个都可以在2到3 GB之间达到最高。(可悲的是,这台机器的RAM+交换的最高容量略低于3.5 GB。这是一个32位操作系统。这是

    因此,在这方面确实没有限制 Linux下的大小,而不是人工的大小。。。



    在Mac上,有一个硬堆栈大小限制 65532千字节 . 这与事物在记忆中的布局有关。


    Normally, you think of an idealized system as having STACK at one end of the memory address space, HEAP at the other, and they build towards each other. When they meet, you are out of memory.

    苹果电脑似乎坚持自己的观点 共享系统库 以固定的偏移量限制两侧。你仍然可以跑步 使用“unlimit stacksize”,因为他只分配8MIB(<64MIB)之类的数据。 但他会用完的 堆栈 .

    Sorry kid. Here's a Nickel. Go get yourself a better OS.

    There are workarounds for the Mac. But they get ugly and messy and involve tweaking the kernel or linker parameters.



    只要你把东西推到桌子上 堆栈

    堆栈

    相反,作为

    回避的理由 存储这只是编码时需要注意的事情。


    这带来了


    • 如果你有大量的小额分配。

    然后,您可以得到大量的变量,这些变量都在代码的一个小的局部区域内使用,分散在许多虚拟内存页上。(例如,您在这个2k页面上使用了4个字节,在那个2k页面上使用了8个字节,以此类推,整个页面都是如此……)

    所有这些都意味着您的程序需要交换大量页面才能运行。或者它会不断地交换页面。(我们称之为打击。)

    堆栈


    下一个话题。线程:


    是的,默认情况下线程仅限于非常小的堆栈。

    单个线程堆栈过大将是一个问题!

    pthread_t       threadData;
    pthread_attr_t  threadAttributes;
    
    pthread_attr_init( & threadAttributes );
    ASSERT_IS( 0, pthread_attr_setdetachstate( & threadAttributes,
                                                 PTHREAD_CREATE_DETACHED ) );
    
    ASSERT_IS( 0, pthread_attr_setstacksize  ( & threadAttributes,
                                                 128 * 1024 * 1024 ) );
    
    ASSERT_IS( 0, pthread_create ( & threadData,
                                   & threadAttributes,
                                   & runthread,
                                   NULL ) );
    

    关于 马丁·约克 堆栈帧:


    当我想到 堆叠框架 堆叠框架 由返回地址、参数和本地数据组成。

    我从来没有看到过任何限制的大小 . 这是有限制的 总的来说,但这就是全部 堆叠框架

    There's a nice diagram and discussion of stack frames over on Wiki.



    在Linux/Unix/MacOS/Darwin/BSD下,可以更改最大值 以编程方式以及 (tcsh)或 文件描述符 (猛击):

    struct rlimit  limits;
    limits.rlim_cur = RLIM_INFINITY;
    limits.rlim_max = RLIM_INFINITY;
    ASSERT_IS( 0, setrlimit( RLIMIT_STACK, & limits ) );
    




        2
  •  29
  •   Peter Mortensen icecrime    14 年前

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

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

    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
    

    如您所见,只要还有空间,就可以在堆栈上放置任何内容。否则,你会看到这个网站的名字。

        3
  •  9
  •   P Daddy    16 年前

    Push pop ebp

        4
  •  5
  •   Peter Mortensen icecrime    14 年前

    default size is 1 MB

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

    堆栈没有划分为整数大小的块。它只是一个扁平的字节数组。它由类型为size\u t(非int)的“整数”索引。如果创建一个适合当前可用空间的大型堆栈对象,它将通过向上(或向下)移动堆栈指针来使用该空间。

    正如其他人指出的,最好对大型对象使用堆,而不是堆栈。这避免了堆栈溢出问题。

    编辑:

        5
  •  3
  •   Salman A    16 年前

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

    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 MSDN还为一些不同的调用冲突提供了一些很好的图表,包括 Sample Code resulting stack diagrams .

        6
  •  2
  •   Jeff Shannon    16 年前

    正如其他人所说,还不清楚你所说的“大型物体”是什么意思。。。然而,既然你问

    它们只是占用多个堆栈吗 “吃角子老虎”?

    我假设你只是指大于整数的任何东西。不过,正如其他人所指出的,堆栈没有整数大小的“插槽”——它只是内存的一部分,每个字节都有自己的地址。编译器根据变量的地址跟踪每个变量 第一 该变量的字节——这是使用运算符地址时得到的值( &var ),指针的值就是其他变量的地址。编译器也知道每个变量是什么类型(你在声明变量时告诉它),它知道每个类型应该有多大——当你编译程序时,它会做任何必要的数学运算来计算调用函数时这些变量需要多少空间,并将结果包含在函数入口点代码(PDaddy提到的堆栈帧)中。

        7
  •  1
  •   Sean    16 年前

        8
  •  1
  •   Stefan    16 年前

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

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

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

        9
  •  0
  •   John Mulder    16 年前

        10
  •  0
  •   Kevin Loney    16 年前

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

    例如,如果您有如下内容:

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

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

    Virtual Address Space .

        11
  •  0
  •   Peter Mortensen icecrime    14 年前

    “堆栈是整数的大小”,您的意思是“堆栈指针是整数的大小”。它指向堆栈的顶部,这是一个巨大的内存区域。嗯,比整数大。