gpt4 book ai didi

c++ - 获取动态 C 样式数组的大小与使用 delete[]。矛盾?

转载 作者:IT老高 更新时间:2023-10-28 12:33:12 27 4
gpt4 key购买 nike

我到处都读到,在 C++ 中,仅从指向该内存块的指针获取动态数组的大小是不可能的。

怎么可能没有办法只从指针中获取动态数组的大小,同时又可以通过delete []释放所有分配的内存> 就在指针上,不需要指定数组大小?

delete[]一定知道数组的大小吧?因此,此信息必须存在于某处。不应该吗?

我的推理有什么问题?

最佳答案

TL;DR 运算符 delete[] 破坏对象并释放内存。销毁需要信息 N(“元素数量”)。解除分配需要信息 S(“分配的内存大小”)。 S 总是被存储并且可以被编译器扩展查询。只有在破坏对象需要调用析构函数时才存储 N。如果存储了 N,则存储位置取决于实现。


操作符delete []要做两件事:

a) 销毁对象(如有必要,调用析构函数)和

b) 释放内存。

让我们首先讨论(de)allocation,它被许多编译器(如 GCC)委托(delegate)给 C 函数 mallocfreemalloc 函数将要分配的字节数作为参数并返回一个指针。 free 函数只接受一个指针;字节数不是必需的。这意味着内存分配函数必须跟踪已分配的字节数。可能有一个函数可以查询已分配了多少字节(在 Linux 中可以使用 malloc_usable_size 来完成,在 Windows 中可以使用 _msize 来完成)。 这不是你想要的,因为它不会告诉你数组的大小,而是分配的内存量。由于 malloc 并不一定会提供您所要求的内存,因此您无法根据 malloc_usable_size 的结果计算数组大小:

#include <iostream>
#include <malloc.h>

int main()
{
std::cout << malloc_usable_size(malloc(42)) << std::endl;
}

这个例子给你 56,而不是 42:http://cpp.sh/2wdm4

请注意,将 malloc_usable_size(或 _msize)应用于 new 的结果是未定义的行为。

那么,现在让我们讨论对象的构造破坏。在这里,您有两种删除方式:delete(用于单个对象)和 delete[](用于数组)。在非常旧的 C++ 版本中,您必须将数组的大小传递给 delete[] 运算符。正如你所提到的,现在情况并非如此。编译器跟踪此信息。 GCC 在数组的开头添加了一个小字段,其中存储了数组的大小,以便它知道必须多久调用一次析构函数。你可能会问:

#include <iostream>

struct foo {
char a;
~foo() {}
};

int main()
{
foo * ptr = new foo[42];
std::cout << *(((std::size_t*)ptr)-1) << std::endl;
}

此代码为您提供 42:http://cpp.sh/7mbqq

仅针对协议(protocol):这是未定义的行为,但在当前版本的 GCC 中它可以工作。

因此,您可能会问自己为什么没有查询此信息的功能。答案是 GCC 并不总是存储这些信息。在某些情况下,对象的销毁是一个无操作(并且编译器能够弄清楚)。考虑以下示例:

#include <iostream>

struct foo {
char a;
//~foo() {}
};

int main()
{
foo * ptr = new foo[42];
std::cout << *(((std::size_t*)ptr)-1) << std::endl;
}

这里的答案是不再是 42:http://cpp.sh/2rzfb

答案就是垃圾——代码又是未定义的行为。

为什么?因为编译器不需要调用析构函数,所以不需要存储信息。而且,是的,在这种情况下,编译器不会添加跟踪已创建对象数量的代码。只有分配的字节数(可能是 56,见上文)是已知的。

关于c++ - 获取动态 C 样式数组的大小与使用 delete[]。矛盾?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54783860/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com