如何确定已分配的C缓冲区的大小?

12

我有一个缓冲区,并且想要进行测试,以查看缓冲区是否具有足够的容量,即找出我可以添加到该缓冲区的元素数量。

char *buffer = (char *)malloc(sizeof(char) * 10);

执行以下操作:

int numElements = sizeof(buffer); 

不返回10,有什么方法可以实现吗?


2
可能是[如何获取使用malloc()分配的内存块的大小?](https://stackoverflow.com/q/1208644/608639)的重复问题,[如何从C指针中获取数组的大小?](https://stackoverflow.com/q/232691/608639),并且相关的是[如何以编程方式确定C ++数组的大小?](https://dev59.com/xHVC5IYBdhLWcg3wvT1a) - jww
5个回答

27

对于 GNU glibc:

SYNOPSIS

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

描述

malloc_usable_size()函数返回指针ptr所指向的内存块中可用字节数,该内存块是由malloc(3)或相关函数分配的。


1
注意:这可能会导致严重的开销,因为它取决于malloc的实现。它返回分配的字节数。要获取可用元素的数量,需要进行额外的除法运算。手册明确指出:“由于对齐和最小大小限制,malloc_usable_size()返回的值可能大于分配的大小。尽管应用程序可以覆盖多余的字节而不会产生不良影响,但这不是良好的编程实践:分配中多余字节的数量取决于底层实现。” - Stargateur
当我尝试使用 size_t no_elements = malloc_usable_size(buffer);printf("%ld\n", no_elements); 时,它返回了24。我错过了什么?顺便说一下,(void *buffer) 报错了。 - undefined

18

你不能进行这样的测试。记住你分配了多少内存是你自己的责任。如果缓冲区是由他人提供的,则要求他们同时传递大小信息,并使其成为他们传递正确值或让程序崩溃的责任。


2
你如何确定他们传递了正确的值? - user124384
2
@user124384:你不能这样做。相反,你需要记录你的库的需求,而用户有责任阅读、理解和遵守合同。你不需要对此负责。动态内存调试器(如Valgrind或ASAN)可以帮助验证单个程序,但C语言无法做到这一点。你也可以将你的库隐藏在某些不透明的抽象边界之后,并在幕后执行所有的分配和释放操作,这样用户只能传递不透明的句柄。(但那是另一个问题的答案!) - Kerrek SB

8

buffer只是一个指针,没有大小信息。然而,malloc()例程将保存您所做的分配的大小,因此当您释放它时,它会释放正确数量的空间。因此,除非您想深入了解malloc()的功能,否则建议您自己保存分配的大小(有关可能的实现,请参见其他API答案中的示例)。


5

由于buffer是一个指针(而不是数组),sizeof运算符返回的是指针的大小,而不是它所指向的缓冲区的大小。没有标准的方法来确定这个大小,因此你必须自己进行簿记(即记住你分配了多少)。

顺便说一句,对于其他情况也是如此。

 char *p = "hello, world\n"; /* sizeof p is not 13. */

有趣的是,
 sizeof "hello, world\n"

14是因为二进制中的1110等于十进制的14。

35
你是在把问题反问回问问题的人吗?这并不是我们在这里的目的... - Evert
@Jens 我知道 sizeof() 函数可以返回字符串字面量和数组的正确大小,但是为什么呢?这些大小信息存储在哪里?例如 char*p = "hello" 返回指针的大小,char p[10] 返回十个字符的大小。 - Zebrafish
7
我们在这里是为了得到答案,他给出了一个答案。提出跟进问题是一种合法的教育方式。此外,我认为他的问题不值得批评,因为他的问题的答案就是他刚刚给出的答案,所以他并没有隐藏信息。我想人们可以就留下“给读者的练习”等事项的效力或价值进行辩论,但在这种情况下,它非常简明扼要且相关。 - Wilbur Whateley
3
@WilburWhateley 无好事不被惩罚。我的回答甚至遭到了负评。如果有人思考后续问题,这个问题是通过引导学习者达到自我发现的古老教育方式提出的,他们可能会对字符串字面量和字符串有所了解。 - Jens
1
我在这里学到了一个重要的课程 - C字符串常量是char数组,而不是指向任意大小缓冲区的指针。这是一个很好且重要的观点。 - Motti Shneor

3
struct buffer
{
  void
    *memory

  size_t
    length;
};

void *buffer_allocate( struct buffer *b, size_t length )
{
  assert( b != NULL );

  b->memory = malloc( length )
  b->length = length;

      // TRD : NULL on malloc() fail
      return( b->memory );
}

int buffer_valid( struct buffer *b, size_t length )
{
  assert( b != NULL );

  if( b->memory == NULL or length > b->length )
    return( 0 );

  return( 1 );
}

void *buffer_get( struct buffer *b )
{
  assert( b != NULL );

  return( b->memory );
}

使用API而不是malloc/free,你就不会出错。


如果你真的想聪明一点,你可以编写自己的 malloc 函数,使用系统 malloc 分配四个额外字节,将分配的长度存储在其中,并在此长度后返回指针。然后,你可以编写一个 getSize 方法,使用指针算术运算再次获取它。这样可以让你使用看起来像是 mallocfree 的调用。 - Gort the Robot
我不确定那是否明智 - 这意味着你个人的 malloc 现在在行为上与所有其他人不同。我已经认识到调整核心函数行为是有风险的观点。它是其他所有内容的基础。 - user82238
1
我知道一家公司,他们重写了malloc函数,使得它所分配的所有内存都进入自由列表(freelists),而free函数只是将元素返回到自由列表。这种做法令人震惊,不仅在其实现方式上如此糟糕(鉴于这个想法有多么糟糕,这并不奇怪),而且已经深深地嵌入到代码中,无法被移除。 - user82238
我喜欢这个,因为它揭示了malloc/calloc实际上并没有记录你要求它们分配的内容。这些方法会为您分配足够的空间 - 有时会多一点,并且与您请求的不同对齐,但不会记住您最初请求的元素数量。最好拥有我们自己定义的缓冲区,其中包括其起始指针和长度(以元素计)。 - Motti Shneor

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