【问题标题】:Why is there no built-in function to find the size of a pointer array in C? [closed]为什么在 C 中没有内置函数来查找指针数组的大小? [关闭]
【发布时间】:2016-07-21 23:58:43
【问题描述】:

free() 只需要指针值即可释放分配的内存。这意味着 C 知道分配的内存块有多大。那么为什么没有一些内置函数来查找指针数组的大小呢?

我知道惯例是跟踪数组大小,但鉴于本机已经进行了一些内存管理,我们为什么不利用它为数组提供一个方便的 size() 函数?

【问题讨论】:

  • 因为没有人设计和提出过这样的功能。我预计会有可实施性问题。但也要考虑不是每个指针都是malloc 的结果,所以你会有一个无法诊断的前提条件。
  • sizeof 是一个编译时特性。 free 是一个运行时函数。
  • 该功能由于舍入和细分而几乎没有用处。例如,如果您malloc(3),大多数实现将返回 1 个 16 字节的块。 free 函数只知道指针代表 1 块内存。它不记得您要求 3 个字节。另一方面,您可以malloc(1024) 然后将该内存用于 4 个每个 256 字节的数组。建议的size() 函数无法知道您如何细分内存。
  • @hazrmard:例如:int array1[10]; int array2[20]; int *ptr = rand() % 2 ? array1 : array2;。除非您自己跟踪它,否则无法知道数组 ptr 指向多大。你能实现size() 以便size(ptr) 返回正确的大小吗?

标签: c sizeof


【解决方案1】:

这样的功能是可能的。问题是为什么 C 标准不需要它。

GNU C 库实现提供了一个函数malloc_usable_size(),与您的建议类似。它返回malloc()ed 指针的可用字节数——由于各种原因,它可能大于请求的大小。

目前还不是 100% 清楚为什么标准中没有这样的功能。 1989 年的ANSI C RationaleISO C99 Rationale 中均未提及。

要记住的一点是,实现不需要跟踪malloc() 调用请求的字节数。它通常会将请求四舍五入到更大的值,并且只需要跟踪它。

就此而言,free() 不一定需要知道正在释放的块的大小。它可能只是调用一些不提供该信息的较低级别的函数。或者,例如,分配的块可能被组织成链表,每个分配的大小一个链表; free() 可能会简单地从该列表中释放一个块,而无需知道其大小。

最后,C 程序员几十年来一直没有这样的功能。添加提供它的要求会在所有实现上强加一些(可能相当小的)开销。我认为这种态度是你可以简单地记住你要求多少内存,并根据需要使用这些信息。

如果分配单个对象:

some_type *ptr = malloc(sizeof *ptr);

然后sizeof *ptr 为您提供对象的大小。如果你分配一个数组:

some_type *ptr = malloc(count * sizeof *ptr);

那么sizeof *ptr 只会给你分配数组的单个元素的大小——但如果你记得count 的值,你就可以很容易地计算出请求的总大小。

底线:C 标准可能需要这样的函数,但实际上没有必要。

更新:Kerrek SB 在评论中提出了一个很好的观点,这是我没有想到的。在这里我冒昧的总结一下。

通过指向其初始元素的指针对数组进行操作的函数(并且有很多这样的函数)不必关心数组是如何分配的。提议的size() 函数,与特定于GNU 的malloc_usable_size() 一样,仅在参数指向堆分配数组时才有效。这意味着该函数要么必须假设数组是堆分配的(并且该假设是正确的!),要么被给予额外的信息。在后一种情况下,还不如给定数组的大小,使size() 变得多余。

【讨论】:

  • 再想一想您将如何使用这些信息:假设您正在处理T 的数组。如果用户想使用自动或静态数组,我们需要f(T * arr, size_t len)。如果用户想要使用 malloced 数组,我们需要另一个 f_from_malloc(T * arr)。但是如果用户只想对给定数组的子数组进行操作,我们又需要第一种形式。所以我们很可能无论如何都想提供指针+长度的形式,那么我们从对 malloced 数组的特殊支持中获得什么?
  • @KerrekSB:一个很好的观点。我已将其应用到我的答案中。
  • 分配比请求更大的块是一种典型的方法,例如使用基于池的方法而不是堆。它也不知道是否分配了数组或structunion。或者,如果分配的块被应用程序分割成更小的块(例如,预分配以获得线性地址空间)。 free 只需要块的大小,不需要对象。这将要求将实际对象大小添加到所有对象,而不仅仅是动态分配。 -> 永久内存开销,即使是标量。我们使用 C 是因为我们可以完全控制内存使用。
【解决方案2】:

free() 可以使用内部数据来回收正在释放的内存块,但请注意,此数据不一定包含传递给 malloc()calloc()realloc() 以分配块的确切大小. C 标准未指定检索此信息的函数。

大多数malloc() 实现提供了一个非标准函数来检索已分配块的可用大小:在Glibc 中,该函数是size_t malloc_usable_size(void *ptr);。其他库可能有不同的功能或根本没有功能来检索此信息。

对于检索您拥有指针的数组大小的通用解决方案,这通常是不可能的。在有效的实现中,指针不携带大小信息。可以实现携带此信息的胖指针,但整个系统需要以这种方式编译。一些集成编译器,例如tcc 支持这种方法来提供运行时指针检查。

【讨论】:

    【解决方案3】:

    因为基本上,地址将指向一块内存,其中包含元数据(例如块的大小)。释放该条目实际上会将块标记为可用(如果指针有效)。

    如果调用者之后访问该内存位置,这是未定义的行为。所以即使从这个角度来看,免费也会完成它的工作。

    【讨论】:

    • 我不知道为什么人们在这方面是-1。元数据通常就在 malloc/calloc 返回地址的块之前,但这是一个微不足道的错误。是的,规范中没有的所有内容都是undefined。在现实世界中,程序员应该知道事情是如何运作的。
    • 我认为您认为建议的 size() 函数没有必要,因为 free() 没有它也可以工作。如果这是你的论点,我建议更明确地说明它——但这并不一定意味着size() 函数没有用。
    【解决方案4】:

    free()只需要指针值,因为你只能传递malloc()返回的指针。malloc()会把这个assign的大小写在返回指针的前端地址。当你把指针传递给free(),free()会读取大小,所以free()知道要释放多少空间。因此,没有使用任何函数来查找指针数组的大小。

    【讨论】:

      猜你喜欢
      • 2021-03-11
      • 1970-01-01
      • 2019-11-13
      • 2011-04-25
      • 1970-01-01
      • 1970-01-01
      • 2019-02-03
      • 1970-01-01
      • 2012-08-13
      相关资源
      最近更新 更多