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

初始化大型双数组时出现问题

  •  2
  • ggkmath  · 技术社区  · 14 年前

    #include <stdio.h>
    int main(void)
    {
    double YRaw[4000000]={0}; 
    return 0;
    }
    

    使用GDB,我得到以下评论:

    Program received signal EXC_BAD_ACCESS, Could not access memory.
    Reason: KERN_PROTECTION_FAILURE at address: 0x00007fff5dd7b148
    0x0000000100000f24 in main () at talk2me.c:18
    18 double YRaw[4000000]={0}; // set YRaw[memdepth] so index is 0 to memdepth-1
    

    如果我将YRaw数组的大小减少10倍,一切都会正常。我在系统中有6GB的RAM,所以为什么会出现错误?谢谢,Gkk

    2 回复  |  直到 14 年前
        1
  •  5
  •   RBerteig Keith Adler    14 年前

    4000000 * sizeof(double) 可能在32MB左右。这对于堆栈来说太大了,这就是为什么会出现异常。(简而言之,堆栈溢出。)

    malloc() 要从堆中分配它,请将其设为 static

    一般来说,自动分配只能用于中小型对象。这个阈值很难描述,但在大多数情况下32MB远远高于这个阈值。

    ld ,并找到实际用于为平台布局可执行文件内存的链接器控制文件。)

    text (sometimes called code ) segment . 它包含执行的实际代码,通常包括任何常量数据。一般来说,文本段是不受意外修改的,在某些系统中,物理内存实际上可以在运行相同程序或使用相同共享库的进程之间共享。

    接下来是 data and bss segments

    然后是 stack heap 和它的朋友(通常是 operator new()

    所有全局变量都在 数据 段,仅基于它们是否初始化。如果它们在数据段中,那么它们将直接影响可执行文件的文件大小。无论哪种方式,如果链接器成功,那么它们的存储将保证在进程的生命周期内存在。

    声明的变量 静止的 静止的 在数据段中,但它的名称只有能够看到其定义的作用域知道。

    堆中的分配不能保证在运行时成功。一个进程的总大小有一个上限,由系统策略和硬件体系结构强制执行。例如,在一个典型的32位系统上,该体系结构不能允许任何单个进程的地址超过4GB。

    int foo(void)
    {
        double YRaw[4000000]={0}; 
        // ... do something with huge buffer
    }
    

    在这里, YRaw 是一个局部变量,具有自动存储类。它在函数进入时在堆栈上分配,在函数退出时自动释放。但是,只有当堆栈有足够的空间容纳它时,这才会起作用。如果没有,那么堆栈将溢出,如果您非常幸运,您将得到某种运行时错误来指示这一事实。(如果你运气不好,堆栈仍然溢出,但它写在分配给其他段(可能是文本段)的内存上,聪明的黑客可能可以执行任意代码。)

    static double YRaw2[4000000]={0}; 
    int foo(void)
    {
        static double YRaw3[4000000]={0}; 
        // ... do something with huge buffer
    }
    

    在这里, YRaw2 YRaw3 初始化时,两者都会结束在数据段中,并且在许多平台上会使实际的可执行文件包含您指定为其初始值的400万个0.0值。两个缓冲区之间的唯一区别是范围问题。 仅在函数中可见。

    static double YRaw4[4000000]; 
    int foo(void)
    {
        static double YRaw5[4000000]; 
        // ... do something with huge buffer
    }
    

    在这里, YRaw4 YRaw5 YRaw2型 第3年 程序启动时。

    double YRaw6[4000000]; 
    int foo(void)
    {
        // ... do something with huge buffer
    }
    

    在这里, YRaw6 第4年 除了名称具有全局作用域外,缓冲区可以与其他模块以及该模块中的每个函数共享。它存储在bss段中,就像 第4年 它对文件大小没有影响。

    最后,它可以来自堆。如果它需要在整个运行中存在,就好像它是在编译时分配的一样,可以执行以下操作:

    int foo(void)
    {
        static double *YRaw7 = NULL;
    
        if (!YRaw7) {
            // allocate the buffer on the first use
            YRaw7 = calloc(4000000, sizeof(double));
        }
        // ... do something with huge buffer
    }
    

    在这里, YRaw7

    int foo(void)
    {
        double *YRaw8 = calloc(4000000, sizeof(double));
        assert(YRaw8 != NULL);
        // do something with huge buffer
        // ...
        // but be careful that all code paths that return also
        // free the buffer if it was allocated.
        free(YRaw8);
    }
    

    在这里, YRaw8 与原始示例中预期的自动变量具有相同的生存期,但实际存储在堆中。验证内存分配是否成功可能是明智的做法,就像我调用 assert() ,但是对于内存不足,没有比允许断言失败更好的响应了。

    calloc() 分配缓冲区。这有一个很好的特性,即如果分配成功,内存将被保证初始化为所有零位。但是,这有一个副作用,它(通常)必须写入分配的每个字节才能产生这种效果。这意味着所有这些虚拟内存页不仅被分配给进程,而且每个页都必须被分页并写入每个字节。使用 malloc() 相反,它通常会表现得更好,但代价是内存不能保证清晰。

    我不能给你一个硬性规定,除了大的分配永远不属于堆栈。如果缓冲区需要在进程的生存期内存在,那么 静止的 (无论是在模块范围还是功能范围)通常是正确的答案。

    如果缓冲区需要不同的生存期,只有在运行时才知道大小,或者在运行时响应外部事件而生存和死亡,那么应该在堆上使用 malloc() 和朋友一起最终释放 free() 或者可能是进程的终止。

        2
  •  3
  •   Community CDub    8 年前

    您试图在堆栈上声明整个数组。即使您有一TB的RAM,也只有一小部分固定的RAM专用于堆栈空间。需要在堆上分配大量数据,使用 malloc

    #include <stdio.h>
    int main(void)
    {
      double* YRaw = malloc(4000000 * sizeof(double));
      memset(YRaw, 0, 4000000 * sizeof(double));
    
      /* ... use it ... */
    
      free(YRaw); /* Give the memory back to the system when you're done */
    
      return 0;
    }
    

    另请参见: "What and where are the stack and heap?"