malloc()函数创建一个完整的内存块,然后返回第一个字节的指针。如果它只创建一个内存块,那么它只能存储一个元素对吧?但是malloc()如何在一个内存块中存储多个元素,而当calloc()创建多个内存块并且可以用作动态数组时,这是有意义的。

最佳答案

这是面包和黄油分配内存管理。无论为哪种类型的数据分配/重新分配,它的工作方式都是一样的。动态内存分配提供的三个标准函数是malloc/calloc/realloc。虽然malloc/calloc仅限于分配单个内存块,realloc还可以调整内存块的大小,允许您使用malloc/calloc分配一些初始存储块(或使用realloc分配),然后使用realloc来增加或减少分配的内存量。
与分配内存的每个函数调用一样,必须通过检查返回是否NULL来验证调用是否成功。这就是为什么在使用realloc时,总是使用临时指针!。因此,当(如果)由于内存不足而导致realloc失败时,不会用realloc丢失内存块的地址并创建内存泄漏来覆盖原始指针。
例如,如果您已分配给NULL,则您不会将int *block = malloc (...)作为:

block = realloc (block, 2 * n * sizeof *block);    /* to double size */

如果realloc失败--返回什么,现在realloc保存什么?(提示:block)。因此,您可以使用一个临时指针,例如。
/* always realloc to a temp pointer, or risk losing pointer */
void *tmp = realloc (block, 2 * n * sizeof *block);   /* doubling size */
if (!tmp) {         /* validate every allocation */
    perror ("realloc-block");
    /* handle error - original pointer still good */
}
block = tmp;        /* assign reallocated block */

(注意:使用NULL是因为这是void *tmp的返回类型。它可以有效地realloc匹配int *tmp的类型。关键是临时指针block的类型必须与tmp兼容,以便分配重新分配的块,例如block。对于新的C程序员来说,在C语言中,任何指针都可以在没有显式转换的情况下转换到或从block = tmp;转换。所以void*只是一个要使用的通用临时类型,但是void *tmp的类型在这里是完全正确的)
这样,在验证对block的调用成功之前,不会分配重新分配的块。你可以随意增加。加倍就可以了,或者只加一些固定的数字就可以了,等等。。
处理重新分配任何东西的计划是直截了当的。您可以分配一个块来容纳一些对象。您保留一个已分配号码的计数器(例如realloc)。然后保留一个单独的对象使用数量计数器(比如alloced)。然后,在为另一个对象使用内存(如果填充循环等)之前,只需检查是否used,例如:
if (used == alloced) {          /* check if realloc needed */
    /* realloc here */
    alloced *= 2;               /* update no. allocated (doubling size here) */
}

重新分配并将used == alloced计数器更新为新大小,然后继续。。。
一个简单的例子是,首先为alloced整数分配,然后根据需要重新分配,方法是将当前分配大小加倍以读取和存储无限数量的整数。如前所述,您可以随意增加分配大小,但避免每次添加都重新分配。内存分配是一个相对昂贵的调用。因此,加倍(例如,将2的存储分配增加到2的存储分配)是一个合理的增长率,可以避免在添加对象时重复不必要的重新分配。
#include <stdio.h>
#include <stdlib.h>

#define MAXI 2                  /* initial number of integer to allocate */

int main (void) {

    size_t  alloced = MAXI,     /* counter to track number allocate */
            used = 0;           /* counter to track number used */
    int val = 0,                /* value to read from stdin */
        *block = malloc (alloced * sizeof *block);  /* initial allocation */

    if (!block) {   /* validate every allocation */
        perror ("malloc-block");
        return 1;
    }

    while (scanf ("%d", &val) == 1) {   /* while integer read */
        if (used == alloced) {          /* check if realloc needed */
            /* always realloc to a temp pointer, or risk losing pointer */
            void *tmp = realloc (block, 2 * alloced * sizeof *block);
            if (!tmp) {         /* validate every allocation */
                perror ("realloc-block");
                if (!used)      /* if no ints stored, exit */
                    return 1;
                break;          /* otherwise original pointer still good */
            }
            block = tmp;        /* assign reallocated block */
            alloced *= 2;       /* update no. of ints allocated */
        }
        block[used++] = val;    /* add value to block and update used */
    }

    printf ("no. of bytes allocated : %zu\n"
            "no. of bytes used      : %zu\n"
            "no. of ints stored     : %zu\n",
            alloced * sizeof *block, used * sizeof *block , used);

    free (block);   /* don't forget to free what you allocate */
}

示例使用/输出
我们最初分配给4, 8, 16, 32, 64, 128, ...,所以让我们尝试阅读2-int,看看它是如何运行的。。。
$ ./bin/malloc_realloc < ../dat/100000int.txt
no. of bytes allocated : 524288
no. of bytes used      : 400000
no. of ints stored     : 100000

所以你可以看到我们在过去的100000中已经被realloc超额分配了。不错,但由于您知道所使用的整数的数量,您可以最后一次124288-bytes来调整块的大小,使其正好容纳realloc,方法是:
void *tmp = realloc (block, used * sizeof *block);
...

(这留给您试验——但您不能将100000-int设置为小于存储值的大小)
内存使用/错误检查
在动态分配内存的任何代码中,对于任何分配的内存块,您都有两个职责:(1)始终保留指向内存块起始地址的指针,以便(2)在不再需要时可以释放它。
必须使用内存错误检查程序,以确保您不会尝试访问内存或写入超出或超出已分配块的界限,尝试读取未初始化值或将条件跳转基于未初始化值,最后确认您释放了已分配的所有内存。
对于Linuxrealloc是正常的选择。每个平台都有类似的内存检查程序。它们都很容易使用,只要运行你的程序就可以了。
$ valgrind ./bin/malloc_realloc < ../dat/100000int.txt
==28169== Memcheck, a memory error detector
==28169== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==28169== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==28169== Command: ./bin/malloc_realloc
==28169==
no. of bytes allocated : 524288
no. of bytes used      : 400000
no. of ints stored     : 100000
==28169==
==28169== HEAP SUMMARY:
==28169==     in use at exit: 0 bytes in 0 blocks
==28169==   total heap usage: 17 allocs, 17 frees, 1,048,568 bytes allocated
==28169==
==28169== All heap blocks were freed -- no leaks are possible
==28169==
==28169== For counts of detected and suppressed errors, rerun with: -v
==28169== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

(注意:上面分配的valgrind字节是1,048,568的总和)
始终确认已释放所有已分配的内存,并且没有内存错误。
再看一遍,如果你还有问题,请告诉我。
如果您想检查总分配的总和,可以从shell(假定是POSIX shell)使用2 * sizeof(int), 4 * sizeof(int), ....循环和POSIX数学运算符来完成,例如。
sum=0; for i in $(seq 1 17); do sum=$((sum + (1 << i) * 4)); done; echo $sum
1048568

关于c - 如果malloc()仅分配一个只能为一个变量存储的内存块,如何将其用于创建动态数组?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/57831125/

10-11 22:44
查看更多