为什么C标准库没有提供一个用于获取已分配内存大小的函数?

3
当通过调用malloc()动态分配一定数量的内存时,操作系统会以某种方式内部存储已分配内存的数量(以跟踪使用的内存等),因此当我们不再需要该内存块时,只需提供指向free()的指针即可。
然而,仅凭指针无法以便携式和与操作系统/编译器无关的方式检索该大小。存在一些非便携式方法,例如Windows/Visual C上的_msize或glibc中的malloc_usable_size。因此,唯一的方法仍然是传播所有所需的大小以及相应的指针等,这可能非常容易出错。
因此,问题是:为什么C标准开发人员决定不在标准中包含一个便携式函数?

顺便说一句,问“为什么”可能不太合适,因为至少在某种程度上通常涉及基于观点的事情,但我认为在这里有一些根本性的原因需要这样做。

2个回答

6

因为你可以获得未从malloc等函数返回的东西的指针。

int x = 10;
int * p = &x;

您所说的函数将不得不确定 p 是否从 malloc 返回(可能很昂贵)。 如果不是(就像这种情况),它无法知道分配的空间量。 如果您获得指向由 malloc 分配的内容的指针,但不是由 malloc 分配的确切指针,则还会遇到问题。
int * p = malloc(sizeof(int) * 10);
int * p2 = p + 5;

如果我询问p2的大小,正确的结果是什么?

一种更一致的方法是在需要的位置传递大小。这使您可以使用地址,而不管它们来自何处,包括对某个内存块的偏移(例如,数组,就像我在上面对p2所做的那样)。


1
我不同意第一段,如果那是原因的话,我认为free不存在,C语言没有保护性API(“protective”这个词用得对吗?) - David Ranieri
1
@DavidRanieri,确实像OP所问的函数可能会有与free相同的限制,即参数必须是由分配函数返回的指针,而不是后来释放的。但那又怎样呢?为了容纳像Stephen提到的那些指针的使用,大多数代码需要假设假想的大小函数不能使用,因此提供这样的函数几乎没有什么意义。释放是完全不同的用例。 - John Bollinger
@JohnBollinger,是的,这样做很容易出错,但是在指针方面有什么不容易出错的呢?就算不再深入探讨:使用错误类型解引用void *,这是编译器无法控制的,甚至无法发出警告,但是void *是有用的。 - David Ranieri
@DavidRanieri,问题不在于一个神奇的内存大小函数会出现错误。而是它对大多数情况来说不适用,对几乎所有其他情况来说不必要。同样,这样的函数可以被提供,具有与free相同的限制,但它将很少被使用,正如问题中提出的特定于实现的示例一样。请记住,这不是可行性问题,而是对那些负责编写和维护标准的人的决策过程进行猜测的问题。 - John Bollinger

5
没有技术问题阻止C标准委员会添加一个新的库函数来检索通过malloc()calloc()realloc()aligned_alloc()strdup()或任何类似函数返回的有效指针所能访问的字节数。返回的数字不一定是最初传递给分配函数的大小,并且有可能根本没有这些信息,因此返回值0表示该信息不可用。
至今未添加这样一个函数的原因可能是C标准委员会通常很不愿意添加新函数。例如,即使在大多数C库中一直提供稳定实现,也花费了30多年时间才将strdup()最终纳入C标准(它将成为下一个版本的一部分)。
对于任何先前未返回过内存分配函数或已经释放的指针,该函数将具有未定义行为,就像freerealloc一样。关于NULL是否定义,存在争议,但在这种情况下,返回值0似乎是合适的。如果大小未知,则对于不存储此信息的虚拟分配器,返回值0也表示此条件。
以下是GNU lib C中malloc_usable_size手册页的摘要:

NAME

malloc_usable_size - obtain size of block of memory allocated from heap

SYNOPSIS

   #include <malloc.h>
    
   size_t malloc_usable_size(void *ptr);

DESCRIPTION

The malloc_usable_size() function returns the number of usable bytes in the block pointed to by ptr, a pointer to a block of memory allocated by malloc(3) or a related function.

RETURN VALUE

malloc_usable_size() returns the number of usable bytes in the block of allocated memory pointed to by ptr. If ptr is NULL, 0 is returned.

ATTRIBUTES

Multithreading (see pthreads(7)): the malloc_usable_size() function is thread-safe.

CONFORMING TO

This function is a GNU extension.

NOTES

The value returned by malloc_usable_size() may be greater than the requested size of the allocation because of alignment and minimum size constraints. Although the excess bytes can be overwritten by the application without ill effects, this is not good programming practice: the number of excess bytes in an allocation depends on the underlying implementation.

The main use of this function is for debugging and introspection.

SEE ALSO

malloc(3)


返回的值也可能是非零和不正确的。如果另一个线程释放或重新分配了块,因为“allocsize”函数返回... - Martin James
@DanM.:完全不是这样的:对于任何非由分配函数返回或已释放的非空指针,malloc_usable_size 的行为是未定义的。如果您添加了一个偏移量来实现标记指针,则必须从传递给此函数的标记指针中删除此偏移量,以及free()realloc()。假设分配的指针是16字节对齐的,并且您使用4位标记,则应编写malloc_usable_size((void *)((uintptr_t)(x) & ~15))以获取实际对象指针。超过15的偏移量,没有可靠的方法来计算由malloc()返回的原始指针。 - chqrlie
@DanM:如果你使用 mmap 自己映射内存,你可以通过测试指针 x 的低12位(取决于系统页大小)来检测它是否指向页面的开头,但是再次强调,这不是一个通用解决方案,适用于超过4095的偏移量。 - chqrlie
@DanM.:对于通用解决方案,您可以将分配的指针值存储在哈希表或其他快速访问结构中,并查找“x”以检查它是否指向块的开头...使用哈希表性能应该很好,但要小心在分配新块时添加所有指针值,并在释放它们时将它们删除。 - chqrlie
@chqrlie,可能表格的性能不够好。我想要能够在库中“检测”所有分配(即在分配之前添加4-8个字节的信息),但问题是来自“外部”的分配。我希望能够检测何时需要在释放指针之前调整指针,以及何时不需要(例如,如果它来自strdup)。 - Dan M.
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接