Разница между malloc и calloc? - PullRequest
       84

Разница между malloc и calloc?

715 голосов
/ 08 октября 2009

В чем разница между делами:

ptr = (char **) malloc (MAXELEMS * sizeof(char *));

или

ptr = (char **) calloc (MAXELEMS, sizeof(char*));

Когда стоит использовать calloc вместо malloc или наоборот?

Ответы [ 13 ]

796 голосов
/ 08 октября 2009

calloc() инициализирует ноль буфера, а malloc() оставляет память неинициализированной.

EDIT:

Обнуление памяти может занять немного времени, поэтому вы, вероятно, захотите использовать malloc(), если эта производительность является проблемой. Если инициализация памяти важнее, используйте calloc(). Например, calloc() может сохранить ваш звонок на memset().

349 голосов
/ 19 октября 2009

Менее известное отличие состоит в том, что в операционных системах с оптимистичным распределением памяти, таких как Linux, указатель, возвращаемый malloc, не поддерживается реальной памятью до тех пор, пока программа не коснется ее.

calloc действительно затрагивает память (она записывает нули в нее), и, таким образом, вы будете уверены, что ОС поддерживает выделение с помощью фактической оперативной памяти (или подкачки). По этой же причине он медленнее, чем malloc (он не только обнуляет его, ОС также должна находить подходящую область памяти, возможно, заменяя другие процессы)

См., Например, этот вопрос SO для дальнейшего обсуждения поведения malloc

104 голосов
/ 13 августа 2010

Одним из часто пропускаемых преимуществ calloc является то, что (соответствующие реализации) это поможет защитить вас от целочисленных уязвимостей переполнения. Сравните:

size_t count = get_int32(file);
struct foo *bar = malloc(count * sizeof *bar);

против

size_t count = get_int32(file);
struct foo *bar = calloc(count, sizeof *bar);

Первое может привести к незначительному выделению и последующим переполнениям буфера, если count больше SIZE_MAX/sizeof *bar. В этом случае последний автоматически потерпит неудачу, поскольку большой объект создать невозможно.

Конечно, вам, возможно, придется искать несоответствующие реализации, которые просто игнорируют возможность переполнения ... Если это касается платформ, на которые вы ориентируетесь, вам все равно придется выполнить ручной тест на переполнение .

32 голосов
/ 15 августа 2013

Документация делает calloc похожим на malloc, который просто инициализирует память нулем; это не основная разница! Идея calloc состоит в том, чтобы избежать семантики копирования при записи для выделения памяти. Когда вы выделяете память с помощью calloc, все это отображается на одной физической странице, которая инициализируется нулем. Когда любая из страниц выделенной памяти записывается в физическую страницу, выделяется. Это часто используется для создания ОГРОМНЫХ хеш-таблиц, например, поскольку пустые части хеш-функции не поддерживаются какой-либо дополнительной памятью (страницами); они с радостью указывают на единственную инициализированную нулями страницу, которая может быть разделена между процессами.

Любая запись в виртуальный адрес отображается на страницу, если эта страница является нулевой страницей, выделяется другая физическая страница, там копируется нулевая страница и поток управления возвращается клиентскому процессу. Это работает так же, как файлы, отображенные в память, виртуальная память и т. Д. Работают ... он использует пейджинг.

Вот одна история оптимизации по теме: http://blogs.fau.de/hager/2007/05/08/benchmarking-fun-with-calloc-and-zero-pages/

26 голосов
/ 19 октября 2009

Нет разницы в размере выделенного блока памяти. calloc просто заполняет блок памяти физическим шаблоном с нулевыми битами. На практике часто предполагается, что объекты, расположенные в блоке памяти, выделенном с помощью calloc, имеют начальное значение, как если бы они были инициализированы литералом 0, т. Е. Целые числа должны иметь значение 0, переменные с плавающей запятой - значение 0.0, указатели - соответствующее значение нулевого указателя и т. Д.

Тем не менее, с педантичной точки зрения, calloc (а также memset(..., 0, ...)) гарантированно правильно инициализирует (с нулями) объекты типа unsigned char. Все остальное не гарантируется должной инициализацией и может содержать так называемое представление ловушки , которое вызывает неопределенное поведение. Другими словами, для любого типа, отличного от unsigned char, вышеупомянутая схема «все нули» может представлять недопустимое значение, представление ловушки.

Позже, в одном из стандартов Технических исправлений к C99, поведение было определено для всех целочисленных типов (что имеет смысл). То есть формально в текущем языке C вы можете инициализировать только целочисленные типы с callocmemset(..., 0, ...)). Использование его для инициализации чего-либо еще в общем случае приводит к неопределенному поведению с точки зрения языка Си.

На практике calloc работает, как мы все знаем :), но хотите ли вы его использовать (учитывая вышеизложенное), решать только вам. Лично я предпочитаю полностью избегать этого, вместо этого используйте malloc и выполните мою собственную инициализацию.

Наконец, еще одна важная деталь: calloc требуется для вычисления окончательного размера блока внутри путем умножения размера элемента на количество элементов. При этом calloc должен следить за возможным арифметическим переполнением. Это приведет к неудачному распределению (нулевой указатель), если запрошенный размер блока не может быть правильно рассчитан. Между тем, ваша malloc версия не пытается отслеживать переполнение. В случае переполнения будет выделено некоторое «непредсказуемое» количество памяти.

20 голосов
/ 28 августа 2013

из статьи Сравнение веселья с calloc () и нулевыми страницами в Блог Георга Хагера

При выделении памяти с помощью calloc () объем запрошенной памяти выделяется не сразу. Вместо этого все страницы, которые принадлежат блоку памяти, связаны с одной страницей, содержащей все нули, с помощью некоторой магии MMU (ссылки ниже). Если такие страницы только для чтения (что было верно для массивов b, c и d в исходной версии эталонного теста), данные предоставляются с одной нулевой страницы, которая, конечно, помещается в кэш. Так много для связанных с памятью циклических ядер. Если страница записывается (независимо от того, как), происходит сбой, отображается «настоящая» страница, а нулевая страница копируется в память. Это называется копированием при записи, хорошо известным подходом к оптимизации (который я даже несколько раз преподавал в своих лекциях по C ++). После этого трюк с нулевым чтением больше не работает для этой страницы, и поэтому производительность была намного ниже после вставки - предположительно избыточного - цикла инициализации.

11 голосов
/ 17 августа 2014

calloc обычно malloc+memset до 0

Обычно немного лучше использовать malloc+memset явно, особенно когда вы делаете что-то вроде:

ptr=malloc(sizeof(Item));
memset(ptr, 0, sizeof(Item));

Это лучше, потому что sizeof(Item) известно компилятору во время компиляции, и компилятор в большинстве случаев заменит его наилучшими инструкциями для обнуления памяти. С другой стороны, если memset происходит в calloc, размер параметра выделения не компилируется в коде calloc, и часто вызывается реальный memset, который обычно содержит код для выполнения byte-by -байт заполнить до длинной границы, затем цикл для заполнения памяти в sizeof(long) кусках и, наконец, побайтовое заполнение оставшегося пространства. Даже если распределитель достаточно умен для вызова некоторого aligned_memset, он все равно будет универсальным циклом.

Одно заметное исключение может произойти, когда вы выполняете malloc / calloc для очень большого куска памяти (несколько power_of_two килобайт), в этом случае выделение может быть сделано непосредственно из ядра. Поскольку ядра ОС обычно обнуляют всю память, которую они отдают по соображениям безопасности, достаточно умный calloc может просто вернуть ее с дополнительным обнулением. Опять же - если вы просто распределяете что-то, что, как вы знаете, мало, вам может быть лучше с malloc + memset с точки зрения производительности.

8 голосов
/ 12 января 2015

Разница 1:

malloc() обычно выделяет блок памяти, и это инициализированный сегмент памяти.

calloc() выделяет блок памяти и инициализирует весь блок памяти равным 0.

Разница 2:

Если вы рассмотрите синтаксис malloc(), он будет принимать только 1 аргумент. Рассмотрим следующий пример ниже:

data_type ptr = (cast_type *)malloc( sizeof(data_type)*no_of_blocks );

Пример: если вы хотите выделить 10 блоков памяти для типа int,

int *ptr = (int *) malloc(sizeof(int) * 10 );

Если вы рассмотрите синтаксис calloc(), он будет принимать 2 аргумента. Рассмотрим следующий пример ниже:

data_type ptr = (cast_type *)calloc(no_of_blocks, (sizeof(data_type)));

Пример: если вы хотите выделить 10 блоков памяти для типа int и инициализировать все это нулем,

int *ptr = (int *) calloc(10, (sizeof(int)));

Сходство:

И malloc(), и calloc() вернут void * по умолчанию, если они не приводятся по типу.!

7 голосов
/ 14 сентября 2012

Есть два отличия.
Во-первых, это количество аргументов. malloc() принимает один аргумент (требуется память в байтах), а calloc() - два аргумента.
Во-вторых, malloc() не инициализирует выделенную память, в то время как calloc() инициализирует выделенную память нулем.

  • calloc() выделяет область памяти, длина будет равна произведению ее параметров. calloc заполняет память нулями и возвращает указатель на первый байт. Если ему не удается найти достаточно места, он возвращает указатель NULL.

Синтаксис: ptr_var=(cast_type *)calloc(no_of_blocks , size_of_each_block); т.е. ptr_var=(type *)calloc(n,s);

  • malloc() выделяет один блок памяти REQUSTED SIZE и возвращает указатель на первый байт. Если он не может найти требуемое количество памяти, он возвращает нулевой указатель.

Синтаксис: ptr_var=(cast_type *)malloc(Size_in_bytes); Функция malloc() принимает один аргумент, который является количеством байтов для выделения, в то время как функция calloc() принимает два аргумента, один из которых является числом элементов, а другой - количеством байтов, выделяемых для каждого из этих элементов. , Кроме того, calloc() инициализирует выделенное пространство нулями, а malloc() - нет.

6 голосов
/ 30 июля 2013

Функция calloc(), объявленная в заголовке <stdlib.h>, предлагает несколько преимуществ по сравнению с функцией malloc().

  1. Распределяет память как количество элементов заданного размера, а
  2. Инициализирует выделенную память, чтобы все биты были ноль.
...