【问题标题】:C Language: Why does malloc() return a pointer, and not the value?C 语言:为什么 malloc() 返回的是指针,而不是值?
【发布时间】:2011-12-07 05:23:45
【问题描述】:

根据我对 C 的理解,似乎在尝试初始化时都应该使用 malloc(size),例如,一个直到运行时才知道其大小的数组。

但我想知道为什么函数 malloc() 会返回一个指向变量位置的 指针,以及为什么你甚至需要它。

基本上,为什么 C 不把它全部隐藏起来,这样每当你做这样的事情时:

    // 'n' gets stdin'ed from the user
    ...
    int someArray[n];

    for(int i = 0; i < n; i++)
        someArray[i] = 5;

您无需调用 malloc() 或其他函数就可以做到这一点?其他语言是否这样做(通过完全隐藏内存属性/位置)?我觉得作为一个初学者,处理你使用的变量的内存位置的整个过程只会让程序员感到困惑(而且由于其他语言不使用它,C 似乎使一个简单的初始化过程像这样过于复杂)......

基本上,我想问的是为什么 malloc() 甚至是必要的,因为为什么语言不会在内部为您处理所有这些,而程序员不必担心或不必查看内存。谢谢

*edit: 好吧,也许有一些我不知道的 C 版本允许你放弃使用 malloc() 但我们现在试着忽略它......

【问题讨论】:

  • 您的问题是关于 VLA 还是关于堆上的动态分配?
  • 重新编辑:允许这样做的版本是当前标准 C。忽略它并没有真正意义。
  • VLA 不会让您放弃使用 malloc()。他们可以做malloc() 可以做的一些,但绝不是全部。请参阅下面的答案。

标签: c memory-management malloc


【解决方案1】:

实际上 C99 允许这样做(所以你不是唯一一个想到它的人)。该功能称为 VLA(可变长度数组)。

读取int 然后拥有该大小的数组是合法的:

int n;
fscanf("%d", &n);
int array[n];

当然有限制,因为malloc 使用堆,VLAs 使用堆栈(所以 VLA 不能像 malloced 对象那么大)。

*edit: 好吧,也许有一些我不知道的 C 版本允许你放弃使用 malloc() 但让我们尝试忽略 暂时这样……

所以我们可以专注于火焰?

【讨论】:

  • 不。 VLA 只是alloca 的语法糖,它在堆栈上分配空间,而不是在堆上。
  • @ruslik 是的。但是对于小数组这不是问题,你不同意吗?
  • 当然。只是不清楚问题是关于堆还是 VLA。
  • @ruslik 我被 OP 发布的示例所吸引:int someArray[n];
  • @Als 这就是我们喜欢 C 的原因 :) 如果分配和释放对程序员来说是隐藏的,那么你必须使用垃圾收集器,那就是 Java。
【解决方案2】:

C 是一种编译语言,而不是解释语言。如果您在编译时不知道n,编译器应该如何生成二进制文件?

【讨论】:

  • 调用malloc(n)时生成二进制文件的方式相同?
  • 确实如此 :) 但是,我认为问题不在于 如何 做到这一点,而在于 为什么 内存管理留给程序员首先。
  • 那也不会让你的答案有用;)
  • 谢谢,德尔南。所以看来我的问题似乎比必要的更长,哈哈。也许我应该在一行中问它,例如,“为什么程序员真的需要知道除了他正在操纵的值之外的任何东西?”大声笑。
  • 或“为什么程序员真的需要知道他正在操作的实际值(变量)以外的任何东西?”
【解决方案3】:

也许问题应该是“当您可以使用指针时,为什么还需要 int array[n] 之类的东西?”

毕竟,指针允许您在创建对象的范围之外保持对象的活动,您可以使用指针对数组进行切片和切块(例如 strchr() 返回指向字符串的指针),指针是轻量级的对象,因此将它们传递给函数并从函数中返回它们等很便宜。

但真正的答案是“就是这样”。其他选项也是可能的,证明还有其他语言可以做其他事情(甚至 C99 也允许做不同的事情)。

【讨论】:

    【解决方案4】:

    C 被视为高度发达的低级语言,基本上 malloc 用于动态数组,这是堆栈和队列中的关键组件。对于其他对开发人员隐藏指针部分的语言,它们不能很好地进行硬件相关的编程。

    【讨论】:

      【解决方案5】:

      C 可让您管理程序的每一点。您可以管理何时分配内存;您可以管理何时它被释放;您可以管理如何增加小额分配等。

      如果您不想管理它并让编译器为您完成,请使用另一种语言。

      【讨论】:

      • 谢谢,pmg。但是当你解除分配时,我知道你使用了 free() 但这并不一定会缩小堆。但是是否存在堆会缩小(因此 brk 标记会消退)的情况?前任。假设您释放了与任何其他块相比位于堆中最高地址的块
      • 是和不是。我的意思是:除非您正在为 C 编写编译器/库,否则可用的空闲或最近释放的内存会发生什么变化不是您关心的问题。如果您正在用 C 编写标准库,那么您一开始就没有使用malloc 和朋友的奢侈。
      • 好吧,实际上我想知道当堆中的最后一个块是空闲/未分配时是否可以缩小堆......你会移动 brk 指针吗? (我很好奇这是否就是将内存“返回”回系统所需要的全部......)
      • @Dark:实际上远比这复杂得多——取决于分配的大小和malloc(3) 例程的实现,内存可能是从匿名内存映射中分配的(@987654323 @, MAP_ANONYMOUS) 当调用free(3) 时,它更容易回馈给操作系统:只需munmap(2) 区域上的所有对象都是空闲的。我不认为很多(任何?)malloc(3) 实现会缩小基于brk(2) 的分配——它可能不值得这么复杂。
      【解决方案6】:

      基本上,我想问的是为什么 malloc() 甚至是必要的, 因为为什么语言不能为你处理所有这些 在内部,程序员不必担心或 必须看到内存。

      malloc() 的关键点,它的存在理由,它的函数,如果你愿意的话,就是分配一块内存。我们在 C 中引用内存块的方式是通过它的起始地址,根据定义,它是一个指针。

      C 已经有将近 40 年的历史了,它还没有一些更现代的语言那么“高级”。一些语言,如 Java,试图通过对程序员隐藏指针和显式内存管理来防止错误并简化编程。 C不是这样的。为什么?因为事实并非如此。

      【讨论】:

      • 堆栈和堆之间的主要区别在于堆栈非常快,但是一旦退出分配它们的块(它们是本地的),空间就会被释放。堆上的变量可以永远存在,但malloc 速度慢并且浪费更多空间。这就是 C 允许您选择存储类型的原因。
      • Java 只使用带有隐式释放的指针,因此对象 a = 新对象和 b = a 引用 b 到 a,不会复制它
      【解决方案7】:

      您的问题的简短回答是思考这个问题:如果您还需要准确控制何时释放内存怎么办?

      【讨论】:

      • 为什么不让垃圾收集器这样做,即使 gc 在不再需要内存时不能准确地释放?
      • 可以,某些语言可以,对于某些应用程序,这是最佳解决方案。
      【解决方案8】:
      基本上,我想问的是为什么 malloc() 甚至是必要的,因为为什么语言不能在内部为您处理所有这些,而程序员不必担心或必须查看内存。谢谢

      C 的标志之一是它的简单性(C 编译器相对容易实现);使语言变得简单的一种方法是强制程序员自己进行所有的内存管理。显然,其他语言确实为您管理堆上的对象 - Java 和 C# 是现代示例,但这个概念一点也不新鲜。 Lisp 实现已经这样做了几十年。但这种便利是以编译器复杂性和运行时性能为代价的。

      Java/C# 方法有助于消除 C 特有的所有类型的内存管理错误(内存泄漏、无效的指针取消引用等)。出于同样的原因,C 提供了对内存管理的一定程度的控制,允许程序员实现其他语言难以(并非不可能)匹配的高水平性能。

      【讨论】:

      • 感谢您的回答,约翰。我想一个扩展的问题是,控件给实际的 C 程序员本人带来了什么样的好处? (而不仅仅是运行时性能)
      【解决方案9】:

      如果动态分配的唯一目的是分配可变长度数组,那么malloc() 可能就没有必要了。 (但请注意,malloc() 早在可变长度数组被添加到语言之前就已经存在了。)

      但是在创建对象时,VLA 的大小是固定的(在运行时)。它无法调整大小,并且只有在您离开声明它的范围时才会被释放。 (而且 VLA 与 malloc() 不同,没有报告分配失败的机制。)

      malloc() 为您提供很多更多的灵活性。

      考虑创建一个链表。每个节点都是一个结构,包含一些数据和指向列表中下一个节点的指针。您可能事先知道每个节点的大小,但您不知道要分配多少个节点。例如,您可以从文本文件中读取行,为每一行创建并附加一个新节点。

      您还可以将malloc()realloc() 一起使用来创建一个缓冲区(例如,unsigned char 的数组),其大小可以在创建后更改。

      是的,有些语言不公开指针,而是为您处理内存管理。

      其中很多都是用 C 实现的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2022-01-02
        • 2022-11-10
        • 2011-10-04
        • 2013-06-27
        • 1970-01-01
        • 2020-09-09
        • 2011-05-24
        相关资源
        最近更新 更多