对象什么时候获得内存?

时间:2018-07-17 18:11:47

标签: c++ memory gdb stack-overflow

我有这样的功能:

1  int main(){
2    int n,m; cin>>n>>m; // Only for illustration
3    vector<int> v[(int) 1e8] ;
4    // ...
5  }

由于第3行引起的堆栈溢出,我将遇到分段错误。由于我正在学习gdb,所以我在main()和第2行设置了一个断点。

我期望程序不在第3行之前为该对象分配内存,而第1行和第2行应该正确运行。运行第3行后必须发生分段错误。 唉!当运行程序时,它导致在第1行之后出现分段错误。我使用了info locals,它给出了:

v= <error reading variable v (value requires 2400000000 bytes, which 
    is more than max-value-size)>

这意味着程序在进入main之后试图分配内存,但是为什么会这样呢? 我认为变量的作用域是从声明的那一行开始的,因为否则在声明错误之前使用它。 我将其用于编译:

g++ -g temp.cpp -o temp -O0

编辑

  1. 这个巨大的载体是有意的,因为我正在“学习”使用gdb。但是后来我遇到了这个概念性错误。

  2. 我将问题从注释中指出的大向量(1e10)恢复为向量数组。

3 个答案:

答案 0 :(得分:3)

 vector<int> v[(int) 1e8];
     

我希望程序不要在第3行之前为对象分配内存

这是一个错误的假设。

该标准没有明确说明何时获取自动变量的存储。尽管编译器可能会将分配从内部块提升到外部,但可以在块语句的开头获取它。实际上,如果编译器内联扩展函数调用,则即使在执行到达函数调用表达式之前,也可能会分配自动变量。

  

我认为变量的作用域是从声明的那一行开始的

是的。但是,对象的存储可能会存在比该对象的生存期更长的时间。

  

这意味着程序在进入main之后试图分配内存,为什么会这样?

在函数开始时,一次分配函数的所有堆栈对象通常是最有效的。堆栈分配不过是将分配的对象的大小添加到堆栈指针而已。

答案 1 :(得分:0)

在堆栈上创建变量是很常见的(但据我所知还不能保证),所有的一切都在执行任何其他语句之前在每个块中创建。而且由于该程序无法在堆上保留1e8向量实例,因此在执行任何其他语句之前,程序将通过保留堆栈而失败,这将导致程序失败。

失败不是在创建向量类本身的实例上,因为构造函数将在第3行被调用。但是如前所述,程序会崩溃,因为堆栈会根据需要变小。

BTW:只要执行结果与程序中编写的结果相同,编译器就可以对所有指令重新排序。

答案 2 :(得分:0)

从一个角度来看,正确的答案是引用语言标准中有关对象寿命的文字。

我认为,对这里发生的事情进行低级解释会更有用。您正在分配一亿个向量的数组。 (您确定要这样做吗,而不是一个包含一亿个元素的向量?)因为它位于main内,所以编译器可以静态地初始化它们,但是在这种情况下,它正在尝试从堆栈中分配它们。

此操作的方法因目标而异。在大多数调用约定中,它是通过在调用函数时从堆栈指针中减去堆栈帧和所有局部变量所需的字节数来实现的。这比尝试在函数内设置第二个帧指针要有效得多,这就是为什么嵌套块内的任何局部变量通常在调用函数时也会分配其内存的原因。

即使一个空向量也需要相当数量的字节-至少需要一个指针和一个size_t。因此,根据是32位还是64位程序乘以一亿,每个八个或十六个字节就差不多是一千兆字节或两个。在这种情况下,该内存多于一个函数被允许脱离堆栈的空间,因此该程序在尝试为main()设置堆栈帧时立即崩溃。

这与调用对象的构造函数是分开的,对象的构造函数可能具有并取决于副作用,因此不允许提前完成。您可以自己分配这两个步骤,方法是分配一个内存缓冲区,然后使用该缓冲区作为地址调用放置new