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;
正确打印的事实?
答案 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
返回的确切指针。其他一切都是非法的。