C ++中的内存分配问题

时间:2011-07-03 12:10:57

标签: c++ memory-management

int main() {

  char** k;

  k = new char*;

  char* k1 = "abc";
  char* k2 = "def";

  *k = k1;
  *(k + 1) = k2;
  delete [] (k + 1);


}

错误:分段错误

有人可以解释为什么我在解放时会出现分段错误(k + 1)吗?我能够毫无问题地释放k。

ADD:在答案中,据说我不能delete [] (k + 1),因为我没有使用新的;但是,您如何解释cout<<*(k + 1)<<endl;正确打印的事实?

4 个答案:

答案 0 :(得分:3)

k = new char*;

这只为一个char*分配了存储空间。

*(k + 1) = k2;

这会尝试假装分配了两个char*。这可能不是段错的站点,但这是错误。

delete [] (k + 1);

在这里,您尝试delete[]您没有new[]的内容,另一个错误。

编辑:内心深处,内存以大块(例如页面)分配。因此,当您分配一小部分存储空间时,它周围的内存很可能也是有效的。但是,访问它仍然非常无效。

更重要的是,当您说new char*之类的内容时,这会变成对operator new(sizeof(char*))的调用。假设操作系统为地址0x12340000分配了一个新的4K物理RAM页面。内存管理器需要一个小结构来跟踪块,如:

struct mem_block_info {
    void* next_block;
    size_t block_size;
};

所以它把这个结构放在0x12340000。在此之后,它会立即放置您请求的存储,因此(假设这是一个32位计算机)它会返回0x12340008的指针,因为sizeof(void*) == sizeof(size_t) == 4。然后它需要在存​​储之后放置一个标题来跟踪该4K页面的未使用部分,因此当您想要另一个char*时,它不会通过分配另一个4K页面而浪费内存。该标题位于您分配的块0x1234000C的末尾之后的地址。尘埃落定之后,new char*已经把它放在记忆中了:

Address    Data
0x12340000 0x00000000
0x12340004 0x00000001
0x12340008 uninitialized; could be anything
0x1234000C 0x00000000
0x12340010 0x00000FF4

空指针指示已分配和空闲块链接列表的结束。

所以当你这样做时:

*(k + 1) = k2;

k + 1 == 0x1234000C是空闲块的next_block指针,你只是用无效值覆盖它(最可能的是只读内存中字符串的地址)。这不会立即导致分段错误,但是当内存管理器尝试遍历空闲块列表时,它将最终查看该字符串并将其误解为块头,然后从那里转到next_block。是一个无效的地址,繁荣,段错误。

答案 1 :(得分:2)

delete只能new完全从new[]返回的内容,同样适用于delete[]delete(void*)

未定义的行为 以传递任何未被new返回的地址。 以下是C ++标准的引用。

§3.7.4.2-3

  

如果通过抛出异常终止释放函数,则行为未定义。提供给解除分配函数的第一个参数的值可以是空指针值;如果是这样,并且如果解除分配功能是标准库中提供的功能,则该呼叫无效。否则,标准库中提供给operator new(std::size_t)的值应该是先前调用标准库中的运算符new(std::size_t, const std::nothrow_-t&)或运算符delete[](void*)返回的值之一,以及值提供给标准库中的运算符new[](std::size_t)应该是之前调用标准库中的运算符new[](std::size_t, const std::nothrow_t&)或运算符{{1}}返回的值之一。

答案 2 :(得分:1)

k + 1未使用new[]创建。你怎么能delete[]呢?

他们总是成对出现。

答案 3 :(得分:1)

您只能delete new返回的确切指针。其他一切都是非法的。