【问题标题】:Declaration and Allocation申报与分配
【发布时间】:2014-09-17 22:53:35
【问题描述】:

我对 C 的分配有一些疑问。

#include <stdlib.h>

typedef struct MyStruct {
    char CharsInMyStruct[50];
} MyStruct;

int main(void) {

    struct MyStruct * s;

    s = malloc(100 * sizeof *s);
}

有人告诉我分配是“智能”的,因为在我使用内存之前,空间并不是“真正”(物理)分配的。内存是在我使用 MyStruct 的第一个元素时物理分配的,还是在我使用每个元素时分配的?

我认为如果我需要动态读取文件并复制动态结构中的每一行,我可以分配 100 个元素,然后在读取文件后重新分配正确的维度,所以我不必阅读两次以预先知道要为每行分配多少或重新分配多少。这可能/允许吗?这是一个好的解决方案吗?

编辑:对不起,我太关心理论了,我忘了添加一个像样的代码。也许这个问题还不够清楚,但我不需要知道如何编写我在想的东西;我只需要知道内存是如何分配的。

【问题讨论】:

  • 您的代码甚至不应该编译。你应该告诉我们MyStruct 是什么。并且可能在malloc 调用中使用sizeof(MyStruct)向我们展示您的实际代码;一般来说C或者它的分配并不聪明。
  • 您的代码 sn-p 很奇怪(无法编译,不清楚它真正应该做什么)。这无助于使您的问题更清楚,而是相反。您应该解释一下代码 sn-p 应该说明什么/...,目前它主要是令人困惑的。
  • 我没有发布代码,因为我认为其余代码(读取函数、realloc 函数、结构中元素的副本等)与问题无关。我想知道内存分配在较低级别是如何工作的,我对代码本身没有问题!
  • struct MyStruct *var; var = malloc(sizeof(struct MyStruct) * 100);

标签: c linux realloc alloc


【解决方案1】:

原则上,调用malloc() 会立即分配请求的内存。如果它不能这样做,malloc() 调用会返回一个空指针让调用者知道。

实际上,尽管某些系统(包括 Linux)使用“延迟分配”。 malloc() 调用会立即分配一系列内存地址,但不一定立即分配实际内存页面。当您的程序稍后访问“分配的”内存时,内核会分配物理内存。这个想法是避免分配可能永远不会使用的内存,特别是当forking 进程时(子进程继承其父内存的副本,但通常exec()s 在使用它之前是另一个可执行文件。)

这种方法的一大缺点是,如果在您尝试使用内存时可用,则没有干净的方法来报告故障。当系统内存不足时,"OOM killer" 可能会开始杀死进程——不一定是执行分配的进程。

有人认为这种行为会导致 Linux 上的 C 实现不符合标准。

我对非 Linux 系统不太熟悉。

不过,对于大多数实际用途,只要您不分配 大量 内存,您就可以忽略所有这些。您应该始终通过返回空指针来检查malloc() 是否报告失败,并采取一些措施(即使您只是终止程序)。如果你在一个无限循环中做一个大的malloc,很可能会发生不好的事情。但是,无论出于何种原因,当资源短缺时,程序很可能会开始崩溃。您最好的防御措施是设计您的程序,使其在意外终止时不会使事情处于不一致的状态。

【讨论】:

  • 可以通过/proc/sys/vm/overcommit_memory在Linux上禁用内存过量使用
  • 谢谢Keith,内存是一次性分配的吗?就我而言,如果我使用第一个元素,linux 会为 100 个结构分配内存还是只为一个?
  • @user3574984:据我所知,内存是按页分配的。我不知道“页面”有多大;它可能因一个系统而异。对于您的示例,100 个struct MyStruct 对象大约是 5000 字节,这很小,但是如果操作系统使用 4096 字节页面,则在您访问第一个元素时它可能只分配一个页面。 (同样,就 C 语言标准而言,当您调用 malloc() 时,所有这些都已分配。在某种程度上,这取决于“已分配”的真正含义。)
【解决方案2】:

正如评论所说:

struct myStruct * structLink;
structLink = malloc (sizeof(struct myStruct));

完成后:

free(structLink);

解除分配。

【讨论】:

  • 我将尝试重新提出我的问题:如果我为 100 个结构分配内存,是在声明中物理分配内存还是在我第一次使用 StructLink 时发生有效分配?
  • 我相信一旦使用 malloc 就会分配内存。不过要对此进行测试,请使用 malloc 命令并尝试打印 structLink 的内存地址。然后,利用 structLink 再次打印地址。这可能会为您解决问题!
【解决方案3】:

正如其他人所说,逻辑上malloc() 分配空间并且可以立即使用。正如Keith Thompson 在他的answer 中指出的那样,Linux 对内存分配采取了一种惰性方法,因此您可以(如果您不走运)分配空间,检查它是否已分配,但稍后仍然会崩溃,因为内存没有毕竟可用。

  1. 是的,分配 100 个条目,从文件中读取以填充其中一些条目,然后 realloc() 如果毕竟少于 100 个条目,则将数组缩小到所需的大小是合法的。

  2. 您还可以分配一个条目数组(比如从 2​​ 个条目开始),realloc() 空间以根据需要添加更多条目。但是,为了避免二次行为(将旧空间复制到新空间),标准建议是每次realloc() 时将条目数加倍。如果您担心过度分配,您可以随时在循环后应用缩小的realloc()。使用较小的值开始,以便realloc() 代码确实得到执行(测试、调试)。

【讨论】:

  • 感谢您详尽的回答。即使分配是合法的,然后是重新分配,它对程序来说是否安全,或者我应该找到另一种解决问题的方法?像这样动态分配是个坏习惯吗?
  • 它是安全、洁净、可靠和明智的,特别是如果您之前分配的条目是 400 万个,结果文件中只有 200 万个和 10 个条目;您可能会用到将近 200 万个数组条目。
【解决方案4】:

我认为您可能对内存使用的想法有误。当您分配内存时,内存总是在那里,malloc 只是告诉计算机为您保留这么多空间。完成后使用 free() 时,您释放的指针仍将指向该地址。唯一的区别是程序将知道它不再是您可以使用的空间。 (尽管 C 可能仍然允许您,直到您遇到段错误)

至于您的第二段,您执行它的方式取决于实施。虽然我真的不明白为什么每次读取新文件时都不能在旅途中进行 malloc。

如果这不是您要找的,请见谅。

【讨论】:

  • 因为我必须读取一个有 n 行的文件,每行有 x 个字符。我想复制结构中的每一行,但我想为每一行分配确切的空间,所以我使用指向 char 的指针。所以我打开文件,将内容复制到一个静态 char 数组(大约 2k 个元素)中,而不是复制结构中的每一行。我的问题是我想知道每行有多少行或多少个字符,而无需两次读取文件。我不确定它是否清楚。
猜你喜欢
  • 2023-03-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多