Разница в использовании между malloc и calloc

gcc 4.5.1 c89

Я написал этот исходный код для лучшего понимания malloc и calloc.

Я понимаю, но у меня есть несколько вопросов.

dev = malloc(number * sizeof *devices);

равен этому calloc. Меня не интересует очистка памяти.

dev = calloc(number, sizeof *devices);

Что это такое, по сравнению с этим 5 раз в цикле while:

dev = malloc(sizeof *devices);

Я предполагаю, что первый, а второй создает указатель на 5 структурных устройств. И третий создает единый указатель на устройство struct?

Моя программа иллюстрирует это 3 разных метода, скомпилированных и запущенных с valgrind --leak-check = full.

Большое спасибо за любой совет.

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

struct Devices {
#define MAX_NAME_SIZE 80
    size_t id;
    char name[MAX_NAME_SIZE];
};

struct Devices* create_device(struct Devices *dev);
void destroy_device(struct Devices *dev);

int main(void)
{
    size_t num_devices = 5;
    size_t i = 0;
    struct Devices *device = NULL;
    struct Devices *dev_malloc = NULL;
    struct Devices *dev_calloc = NULL;

    for(i = 0; i < num_devices; i++) {
        device = create_device(device);
        /* Assign values */
        device->id = i + 1;
        sprintf(device->name, "Device%zu", device->id);
        /* Print values */
        printf("ID ----- [ %zu ]\n", device->id);
        printf("Name --- [ %s ]\n", device->name);
        /* Test free */
        destroy_device(device);
    }

    printf("\n");
    dev_malloc = malloc(num_devices * sizeof *dev_malloc);
    for(i = 0; i < num_devices; i++) {
        /* Assign values */
        dev_malloc->id = i + 1;
        sprintf(dev_malloc->name, "dev_malloc%zu", dev_malloc->id);
        /* Print values */
        printf("ID ----- [ %zu ]\n", dev_malloc->id);
        printf("Name --- [ %s ]\n", dev_malloc->name);
    }
    /* Test free */
    destroy_device(dev_malloc);

    printf("\n");
    dev_calloc = calloc(num_devices, sizeof *dev_calloc);
    for(i = 0; i < num_devices; i++) {
        /* Assign values */
        dev_calloc->id = i + 1;
        sprintf(dev_calloc->name, "dev_calloc%zu", dev_calloc->id);
        /* Print values */
        printf("ID ----- [ %zu ]\n", dev_calloc->id);
        printf("Name --- [ %s ]\n", dev_calloc->name);
    }
    /* Test free */
    destroy_device(dev_calloc);

    return 0;
}

struct Devices* create_device(struct Devices *dev)
{
    /* Not checking for memory error - just simple test */
    return dev = malloc(sizeof *dev);
}

void destroy_device(struct Devices *dev)
{
    if(dev != NULL) {
        free(dev);
    }
}

Ответы

Ответ 1

, отредактированный для ясности

  • Я предполагаю, что первый и второй создают указатель на 5 структурных устройств. А третий создает единый указатель на структурное устройство?

Первый malloc(number * sizeof(*devices)) выделил бы достаточно памяти для хранения number из Device s. Как уже упоминалось, вы можете рассматривать этот блок как массив Device. Указатель, который вы вернетесь, укажет на начало блока.

int number = 5;
Device *ptr = malloc(number * sizeof(*ptr));
/* stuff */
free(ptr);

Второй, который использует calloc, делает то же самое, а также инициализирует память до 0. Опять же, вы можете использовать обработать блок как массив Device.

int number = 5;
Device *ptr = calloc(number, sizeof(*ptr));
/* stuff */
free(ptr);

Третий, зацикленный 5 раз, приведет к 5 различным указателям для 5 различных блоков, достаточно больших, чтобы хранить один Device каждый. Это также означает, что каждый из 5 указателей должен быть free 'индивидуально.

Device *ptrs[5];
for(int i = 0; i < 5; ++i)
{
    ptrs[i] = malloc(sizeof(*ptrs[i]));
}
/* stuff */
for(int i = 0; i < 5; ++i)
{
    free(ptrs[i]);
}

Ответ 2

calloc(a,b) и malloc(a*b) эквивалентны, за исключением возможности возникновения арифметических переполнений или типов, а также то, что calloc гарантирует, что память заполнена нулем. Либо выделенная память, которая может использоваться для массива элементов a, каждый из которых имеет размер b (или наоборот). С другой стороны, вызов malloc(b) a times приведет к a отдельным объектам размером b, которые могут быть освобождены независимо и которые не находятся в массиве (хотя вы могли бы хранить их адреса в массиве указателей).

Надеюсь, что это поможет.

Ответ 3

malloc (n) выделяет n байтов плюс отступы и накладные расходы.

calloc (m, n) выделяет m * n байтов плюс отступы и накладные расходы, а затем нулевую память.

Что это.

Ответ 4

Первые два создают массив из 5 устройств в непрерывной памяти. Последний malloc, сделанный пять раз, создаст 5 отдельных устройств, которые не гарантируются в непрерывной памяти.

Ответ 5

Все три петли в вашей программе используют только один объект struct Devices за раз. Более поздние выделяют дополнительную память, как если бы они собирались использовать несколько объектов, но затем перезаписывают начало этой памяти. Если вы попытались использовать объект с идентификатором 1 после настройки объекта с идентификатором 2, вы обнаружите, что больше нет объекта с идентификатором 1.

Вместо этого вы можете сделать что-то подобное для обработки выделенной памяти как массива структур:

dev_malloc = malloc(num_devices * sizeof *dev_malloc);
for (i=0; i<num_devices; i++) {
    /* Assign values */   
    dev_malloc[i].id = i + 1;   
    sprintf(dev_malloc[i].name, "dev_malloc%zu", dev_malloc[i].id);   
    /* Print values */   
    printf("ID ----- [ %zu ]\n", dev_malloc[i].id);   
    printf("Name --- [ %s ]\n", dev_malloc[i].name);
}
free(dev_malloc);

Ответ 6

Посмотрите на реализацию calloc, чтобы увидеть различия. Вероятно, это примерно так:

// SIZE_MAX is defined in stdint.h from C99
void *calloc( size_t N, size_t S)
{
    void *ret;
    size_t NBYTES;

    // check for overflow of size_t type
    if (N > SIZE_MAX / S) return NULL;

    NBYTES = N * S;
    ret = malloc( NBYTES);
    if (ret != NULL)
    {
        memset( ret, 0, NBYTES);
    }

    return ret;
}

Ответ 7

Как вы указываете, calloc обнуляет память выделяется, а malloc не работает.

В ваших примерах 1 и 2 каждый выделяет один непрерывный блок из пяти структур (и возвращает указатель на этот блок), тогда как в примере 3 выделяется пять отдельных блоков по одной структуре каждый (и дает вам пять указателей, не связанных друг с другом).