当我在分配的动态数组(使用delete[]
关键字创建)上调用new
时,会出现一个非常奇怪的分段错误。起初它是在我删除全局指针时发生的,但它也发生在以下非常简单的情况下,其中delete[] arr
int main(int argc, char * argv [])
{
double * arr = new double [5];
delete[] arr;
}
我收到以下消息:
*** Error in `./energy_out': free(): invalid next size (fast): 0x0000000001741470 ***
Aborted (core dumped)
除main
函数外,我定义了一些相当标准的函数,以及以下函数(在main
函数之前定义)
vector<double> cos_vector()
{
vector<double> cos_vec_temp = vector<double>(int(2*pi()/trig_incr));
double curr_val = 0;
int curr_idx = 0;
while (curr_val < 2*pi())
{
cos_vec_temp[curr_idx] = cos(curr_val);
curr_idx++;
curr_val += trig_incr;
}
return cos_vec_temp;
}
const vector<double> cos_vec = cos_vector();
请注意,在调用main函数之前,cos_vector
,cos_vec_temp
的返回值将被赋值给全局变量cos_vec
。
问题是,我知道导致错误的原因:cos_vec_temp
应该是一个更大的元素,因为cos_vec_temp[curr_idx]
最终会在向量cos_vec_temp
的末尾访问一个元素。当我在创建时使cos_vec_temp
个元素变大时,不会发生错误。但我不明白为什么它出现在delete[]
的{{1}}。当我运行gdb时,在arr
函数开始处设置断点后,在创建main
之后,在检查变量内容时得到以下输出:
arr
在第一个gdb命令中,我显示了(gdb) p &cos_vec[6283]
$11 = (__gnu_cxx::__alloc_traits<std::allocator<double> >::value_type *) 0x610468
(gdb) p arr
$12 = (double *) 0x610470
向量末尾的元素的内存位置,即cos_vec
。第二个gdb命令显示0x610468
指针的内存位置,即arr
。由于我将0x610470
分配给无效的内存位置double
,因此我知道它必须部分写在0x610468
开头的位置,但这是在0x610470
之前完成的。甚至创建(该函数在arr
之前调用)。那为什么这会影响main
?我原本以为在创建arr
时,它并不“关心”以前对那里的内存位置做了什么,因为它没有被注册为正在使用中。
任何澄清都将不胜感激。
注意:
arr
之前已声明为大小为cos_vec_temp
的动态双数组(与代码中的大小相同,但使用int(2*pi()/trig_incr)
创建)。在这种情况下,我也有如上所述的无效访问,并且当我访问该位置的元素时它也没有给出任何错误。但是当我试图在new
全局变量(类型为delete[]
)上调用cos_vec
时,它也会出现分段错误,但它没有给出我得到的消息上面的案例。
注2:
在你投票使用动态数组之前,我很好奇为什么会这样。我通常使用STL容器及其所有便利(我几乎从不使用动态数组)。
答案 0 :(得分:3)
许多堆分配器将元数据存储在它为内存分配的内存旁边,在内存之前或之后(或两者)。如果你写出一些堆分配内存的边界(并记住std::vector
动态分配堆),你可能会覆盖一些元数据,破坏堆。
这些都不是在C ++规范中实际指定的。所有这一切都说超出界限会导致未定义的行为。分配器执行或存储的内容以及可能存储元数据的位置取决于实现。
对于解决方案,大多数人都会告诉您使用push_back
而不是直接索引,并且将解决问题。不幸的是,这也意味着需要重新分配和复制向量几次。这可以通过reserving预先大约的内存量来解决,然后让额外的杂散元素导致重新分配和复制。
或者当然,可以更好地预测矢量将包含的实际元素数量。
答案 1 :(得分:1)
看起来你正在编写在main之前执行的函数中分配的向量的末尾,稍后会导致未定义的行为。
你应该能够通过在分配向量时向上舍入数字来解决问题(转换为int将数字向下舍入),或者使用push_back而不是索引:
cos_vec_temp.push_back(cos(curr_val));