简体   繁体   English

关于C中动态memory分配的一个技术问题

[英]A technical question about dynamic memory allocation in C

I'm studying dynamic memory allocation in C, and I want to ask a question - let us suppose we have a program that receives a text input from the user.我正在研究 C 中的动态 memory 分配,我想问一个问题 - 假设我们有一个程序可以接收用户的文本输入。 We don't know how long that text will be, it could be short, it could also be extremely long, so we know that we have to allocate memory to store the text in a buffer.我们不知道该文本将有多长,它可能很短,也可能非常长,所以我们知道我们必须分配 memory 来将文本存储在缓冲区中。 In cases in which we receive a very long text, is there a way to find out whether we have enough memory space to allocate more memory to the text?在我们收到很长的文本的情况下,有没有办法找出我们是否有足够的 memory 空间来分配更多的 memory 给文本? Is there a way to have an indication that there is no memory space left?有没有办法表明没有 memory 剩余空间?

You can use malloc() function if it returned NULL that means there no enough mem space but if it returned address of the mem it means there are mem space available example:您可以使用 malloc() function 如果它返回 NULL 这意味着没有足够的内存空间,但如果它返回的内存地址意味着有可用的内存空间示例:

void* loc = malloc(sizeof(string));

ANSI C has no standard functions to get the size of available free RAM. ANSI C 没有标准函数来获取可用空闲 RAM 的大小。 You may use platform-specific solutions.您可以使用特定于平台的解决方案。

C - Check currently available free RAM? C - 检查当前可用的空闲 RAM?

In C we typically use malloc , calloc and realloc for allocation of dynamic memory.在 C 中,我们通常使用malloccallocrealloc来分配动态 memory。 As it has been pointed out in both answers and comments these functions return a NULL pointer in case of failure.正如在答案和评论中指出的那样,这些函数在失败的情况下返回一个 NULL 指针。 So typical C code would be:所以典型的 C 代码将是:

SomeType *p = malloc(size_required);
if (p == NULL)
{
    // ups... malloc failed... add error handling
}
else
{
    // great... p now points to allocated memory that we can use
}

I like to add that on (at least) Linux systems, the return value from malloc (and friends) is not really an out-of-memory indicator.我想补充一点,在(至少)Linux 系统上,来自malloc (和朋友)的返回值并不是真正的内存不足指示器。

If the return value is NULL, we know the call failed and that we didn't get any memory that we can use.如果返回值为 NULL,我们知道调用失败并且我们没有得到任何可以使用的 memory。

But even if the return value is non -NULL, there is no guarantee that the memory really is available.但即使返回值不是-NULL,也不能保证 memory 真的可用。

From https://man7.org/linux/man-pages/man3/free.3.html :https://man7.org/linux/man-pages/man3/free.3.html

By default, Linux follows an optimistic memory allocation strategy.默认情况下,Linux 遵循乐观的 memory 分配策略。 This means that when malloc() returns non-NULL there is no guarantee that the memory really is available.这意味着当 malloc() 返回非 NULL 时,不能保证 memory 确实可用。 In case it turns out that the system is out of memory, one or more processes will be killed by the OOM killer.如果发现系统超出 memory,则 OOM 杀手将杀死一个或多个进程。

We don't know how long that text will be我们不知道该文本将持续多久

Sure we do, we always set a maximum limit.当然,我们总是设置一个最大限制。 Because all user input needs to be sanitised anyway - so we always require a maximum limit on every single user input.因为无论如何都需要对所有用户输入进行清理 - 所以我们总是要求对每个用户输入设置最大限制。 If you don't do this, it likely means that your program is broken since it's vulnerable to buffer overruns.如果您不这样做,则可能意味着您的程序已损坏,因为它容易受到缓冲区溢出的影响。

Typically you'll read each line of user input into a local array allocated on the stack.通常,您会将每一行用户输入读入分配在堆栈上的本地数组中。 Then you can check if it is valid (are strings null terminated etc) before allocating dynamic memory and then copy it over there.然后您可以在分配动态 memory 之前检查它是否有效(字符串 null 是否终止等),然后将其复制到那里。

By checking the return value of malloc etc you'll see if there was enough memory left or not.通过检查 malloc 等的返回值,您将看到是否还有足够的 memory 剩余。

I made a test on linux with 8GB RAM.我在 8GB RAM 的 linux 上进行了测试。 The overcommit has three main modes 0, 1 and 2 which are default, unlimited, and never:过量使用具有三种主要模式 0、1 和 2,它们是默认、无限和从不:

Default:默认:

$ echo 0 > /proc/sys/vm/overcommit_memory 
$ ./a.out
After loop: Cannot allocate memory
size 17179869184
size 400000000
log2(size)34.000000

This means 8.5 GB were successfuly allocated, just about the amount of physical RAM.这意味着 8.5 GB 已成功分配,几乎是物理 RAM 的数量。 I tried to tweak it, but without changing swap, which is only 4 GB.我尝试对其进行调整,但没有更改只有 4 GB 的交换空间。

Unlimited:无限:

$ echo 1 > /proc/sys/vm/overcommit_memory 
$ ./a.out
After loop: Cannot allocate memory
size 140737488355328
size 800000000000
log2(size)47.000000

48 bits is virtual address size. 48 位是虚拟地址大小。 140 TB. 140 TB。 Physical is only 39 bits (500 GB).物理只有 39 位 (500 GB)。

No overcommmit:没有过度使用:

$ echo 2 > /proc/sys/vm/overcommit_memory 
$ ./a.out
After loop: Cannot allocate memory
size 2147483648
size 80000000
log2(size)31.000000

2 GB is just what free command declares as free. 2 GB 正是free命令声明为免费的。 Available are 4.6 GB.可用容量为 4.6 GB。


malloc() fails in the same way if the process's resources are restricted - so this ENOMEM does not really specify much.如果进程的资源受到限制, malloc() 会以同样的方式失败 - 所以这个 ENOMEM 并没有真正指定太多。 "Cannot allocate memory" (aka ENOMEM aka 12) just says "malloc failed, guess why" or rather "malloc failed, NO more MEMory for you now.". “无法分配内存”(又名 ENOMEM aka 12)只是说“malloc 失败,猜猜为什么”或者更确切地说是“malloc 失败,现在不再为您提供 MEMory。”。


Well here is a.out which allocates doubling sizes until error.那么这里是 a.out ,它分配双倍大小直到出错。

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <math.h>

int main() {
    size_t sz = 4096;
    void *p;
    while (!errno) {
        p = malloc(sz *= 2);
        free(p);
    }
    perror("After loop");
    printf("size %ld\n", sz);
    printf("size %lx\n", sz);
    printf("log2(size)%f\n", log2((double)sz));
}

But I don't think this kind of probing is very useful/但我不认为这种探测很有用/

Buffer缓冲

we have to allocate memory to store the text in a buffer我们必须分配 memory 将文本存储在缓冲区中

But not the whole text at once.但不是一次完整的文本。 With a real buffer (not just an allocated memory as destination) you could read portions of the input and store them away (out of memory onto disk).使用真正的缓冲区(不仅仅是分配的 memory 作为目标),您可以读取输入的部分并将它们存储起来(从 memory 到磁盘上)。

Only disadvantage is: if I cannot use a partial input, then all the buffered copying and saving is wasted.唯一的缺点是:如果我不能使用部分输入,那么所有缓冲的复制和保存都被浪费了。

I really wonder what happens if I type and type fast for a couple of billion years -- without a newline.我真的很想知道如果我在数十亿年内快速打字会发生什么——没有换行符。


We can allocate much more than we have as RAM, but we only need a fraction of that RAM: the buffer.我们可以分配比 RAM 更多的内存,但我们只需要 RAM 的一小部分:缓冲区。 But Lundin's answer shows it is much easier (typical) to rely on newlines and maximum length.但 Lundin 的回答表明,依靠换行符和最大长度要容易得多(典型)。

getline(3)获取线(3)

This gnu/posix function has the malloc/realloc built in. The paramters are a bit complicated, because a new pointer and size can be returned by reference.这个 gnu/posix function 内置了 malloc/realloc。参数有点复杂,因为可以通过引用返回一个新的指针和大小。 And return value of -1 can also mean ENOMEM, not end-of-file.返回值 -1 也可以表示 ENOMEM,而不是文件结尾。

fgets() is the line-truncating version. fgets()是行截断版本。

fread() is newline independant, with fixed size. fread()与换行无关,大小固定。 (But you asked about text input - long lines or long overall text, or both?) (但您询问了文本输入 - 长行或长整体文本,或两者兼而有之?)

Good Q, good As, good comments about "live input":好Q,好As,关于“实时输入”的好评:

getline how to limit amount of input as you can with fgets getline 如何像使用 fgets 一样限制输入量

There is no standard library function that tells you how much memory is available for use.没有标准库 function 可以告诉您有多少 memory 可供使用。

The best you can do within the bounds of the standard library is to attempt the allocation using malloc , calloc , or realloc and check the return value - it it's NULL, then the allocation operation failed.您可以在标准库范围内做的最好的事情是尝试使用malloccallocrealloc进行分配并检查返回值 - 它是 NULL,然后分配操作失败。

There may be system-specific routines that can provide that information, but I don't know of any off the top of my head.可能有特定于系统的例程可以提供该信息,但我不知道任何在我脑海中的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM