Разница между malloc и calloc?
В чем разница между выполнением:
ptr = (char **) malloc (MAXELEMS * sizeof(char *));
или же:
ptr = (char **) calloc (MAXELEMS, sizeof(char*));
Когда стоит использовать calloc вместо malloc или наоборот?
12 ответов
calloc()
инициализирует нулем буфер, а malloc()
оставляет память неинициализированной.
РЕДАКТИРОВАТЬ:
Обнуление памяти может занять немного времени, поэтому вы, вероятно, захотите использовать malloc()
если это производительность является проблемой. Если инициализация памяти важнее, используйте calloc()
, Например, calloc()
может спасти вас звонок memset()
,
Менее известное отличие состоит в том, что в операционных системах с оптимистичным распределением памяти, таких как Linux, указатель возвращается malloc
не поддерживается реальной памятью, пока программа не коснется ее.
calloc
действительно затрагивает память (она записывает нули на нее), и, таким образом, вы будете уверены, что ОС поддерживает выделение с помощью оперативной памяти (или подкачки). По этой же причине он медленнее, чем malloc (он не только должен обнулять его, ОС также должна находить подходящую область памяти, возможно, заменяя другие процессы)
Посмотрите, например, этот вопрос SO для дальнейшего обсуждения поведения malloc
Одно часто пропускаемое преимущество 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
, Последний автоматически потерпит неудачу в этом случае, так как большой объект не может быть создан.
Конечно, вам, возможно, придется искать несоответствующие реализации, которые просто игнорируют возможность переполнения... Если это проблема для платформ, на которые вы ориентируетесь, вам все равно придется выполнить ручной тест на переполнение.
Документация делает calloc похожим на malloc, который просто инициализирует память нулем; это не основная разница! Идея calloc состоит в том, чтобы избежать семантики копирования при записи для выделения памяти. Когда вы выделяете память с помощью calloc, все это отображается на одной физической странице, которая инициализируется нулем. Когда любая из страниц выделенной памяти записывается в физическую страницу, выделяется. Это часто используется для создания ОГРОМНЫХ хеш-таблиц, например, поскольку пустые части хеш-функции не поддерживаются какой-либо дополнительной памятью (страницами); они с радостью указывают на единственную инициализированную нулями страницу, которая может быть даже разделена между процессами.
Любая запись в виртуальный адрес отображается на страницу, если эта страница является нулевой страницей, выделяется другая физическая страница, там копируется нулевая страница и поток управления возвращается клиентскому процессу. Это работает так же, как файлы, отображенные в память, виртуальная память и т. Д. Работают... он использует пейджинг.
Вот одна история оптимизации по этой теме: http://blogs.fau.de/hager/2007/05/08/benchmarking-fun-with-calloc-and-zero-pages/
Там нет разницы в размере выделенного блока памяти. calloc
просто заполняет блок памяти физическим шаблоном "все нули". На практике часто предполагается, что объекты, расположенные в блоке памяти, выделены с calloc
имеют начальное значение, как если бы они были инициализированы с литералом 0
т.е. целые числа должны иметь значение 0
переменные с плавающей точкой - значение 0.0
, указатели - соответствующее значение нулевого указателя и так далее.
С педантичной точки зрения, хотя, calloc
(так же как memset(..., 0, ...)
) гарантируется только правильная инициализация (с нулями) объектов типа unsigned char
, Все остальное не гарантируется должной инициализацией и может содержать так называемое представление ловушек, которое вызывает неопределенное поведение. Другими словами, для любого типа, кроме unsigned char
вышеупомянутая схема "все нули" может представлять недопустимое значение, представление прерывания.
Позже, в одном из стандартов Технических исправлений к C99, поведение было определено для всех целочисленных типов (что имеет смысл). Т.е. формально, в текущем языке C вы можете инициализировать только целочисленные типы с calloc
(а также memset(..., 0, ...)
). Использование его для инициализации чего-либо еще в общем случае приводит к неопределенному поведению с точки зрения языка Си.
На практике, calloc
работает, как мы все знаем:), но хотите ли вы использовать его (учитывая вышеизложенное), решать только вам. Я лично предпочитаю избегать этого полностью, используйте malloc
вместо этого и выполнить мою собственную инициализацию.
Наконец, еще одна важная деталь заключается в том, что calloc
требуется для внутреннего расчета окончательного размера блока путем умножения размера элемента на количество элементов. Делая это, calloc
необходимо следить за возможным арифметическим переполнением. Это приведет к неудачному распределению (нулевой указатель), если запрошенный размер блока не может быть правильно рассчитан. Между тем, ваш malloc
версия не пытается следить за переполнением. Он выделит некоторый "непредсказуемый" объем памяти в случае переполнения.
Из статьи Сравнение веселья с calloc() и нулевыми страницами в блоге Георга Хагера
При выделении памяти с помощью calloc() объем запрошенной памяти выделяется не сразу. Вместо этого все страницы, которые принадлежат блоку памяти, связаны с одной страницей, содержащей все нули, с помощью некоторой магии MMU (ссылки ниже). Если такие страницы только для чтения (что было верно для массивов b, c и d в исходной версии эталонного теста), данные предоставляются с одной нулевой страницы, которая, конечно же, помещается в кэш. Так много для ядер, связанных с памятью. Если страница записывается (независимо от того, как), происходит сбой, отображается "настоящая" страница, а нулевая страница копируется в память. Это называется копирование при записи, хорошо известный подход к оптимизации (который я даже несколько раз преподавал в своих лекциях по C++). После этого трюк с нулевым чтением больше не работает для этой страницы, и поэтому производительность была намного ниже после вставки - предположительно избыточного - цикла инициализации.
Количество блоков:
malloc () назначает один блок запрошенной памяти,
calloc() назначает несколько блоков запрошенной памяти
Инициализация:
malloc () - не очищает и не инициализирует выделенную память.
calloc () - инициализирует выделенную память нулем.
Скорость:
malloc () работает быстро.
calloc () медленнее, чем malloc().
Аргументы и синтаксис:
malloc () принимает 1 аргумент:
байты
- Количество выделяемых байтов
calloc () принимает 2 аргумента:
длина
- количество выделяемых блоков памяти
- байты
- количество байтов, которые будут выделены в каждом блоке памяти
void *malloc(size_t bytes);
void *calloc(size_t length, size_t bytes);
Способ распределения памяти:
функция malloc назначает память желаемого "размера" из доступной кучи.
Функция calloc назначает память, размер которой равен 'num * size'.
Значение в имени:
Имя malloc означает "выделение памяти".
Имя calloc означает "непрерывное выделение".
calloc
как правило, malloc+memset
до 0
Как правило, немного лучше использовать malloc+memset
явно, особенно когда вы делаете что-то вроде:
ptr=malloc(sizeof(Item));
memset(ptr, 0, sizeof(Item));
Это лучше, потому что sizeof(Item)
Это известно компилятору во время компиляции, и компилятор в большинстве случаев заменит его наилучшими инструкциями для обнуления памяти. С другой стороны, если memset
происходит в calloc
размер параметра распределения не компилируется в calloc
код и реал memset
часто вызывается, который обычно содержит код для побайтового заполнения до длинной границы, чем цикл для заполнения памяти sizeof(long)
куски и, наконец, побайтовое заполнение оставшегося пространства. Даже если распределитель достаточно умен, чтобы вызвать aligned_memset
это все еще будет общий цикл.
Одно заметное исключение может произойти, когда вы выполняете malloc / calloc для очень большого куска памяти (некоторые power_of_two килобайта), в этом случае выделение может быть сделано непосредственно из ядра. Поскольку ядра ОС обычно обнуляют всю память, которую они отдают по соображениям безопасности, достаточно умный calloc может просто вернуть ее с дополнительным обнулением. Опять же - если вы просто выделяете что-то, что, как вы знаете, мало, вам может быть лучше с malloc + memset с точки зрения производительности.
Есть два отличия.
Во-первых, это количество аргументов. 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()
не.
Разница 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 и инициализировать все это в ZERO,
int *ptr = (int *) calloc(10, (sizeof(int)));
Сходство:
Malloc () и calloc () по умолчанию возвращают void*, если они не приводятся по типу.!
malloc()
выделяет блок памяти заданного размера (в байтах) и возвращает указатель на начало блока.
void *malloc(size_t size);
malloc()
не инициализирует выделенную память.
calloc()
выделяет память, а также инициализирует выделяемую память для всех нулевых битов.
void *calloc(size_t num, size_t size);
calloc()
функция, которая объявлена в <stdlib.h>
заголовок предлагает несколько преимуществ по сравнению с malloc()
функция.
- Он выделяет память как количество элементов заданного размера, и
- Он инициализирует память, которая выделена так, что все биты равны нулю.
malloc()
а также calloc()
являются функциями из стандартной библиотеки C, которые позволяют динамическое выделение памяти, а это означает, что они оба позволяют выделение памяти во время выполнения.
Их прототипы следующие:
void *malloc( size_t n);
void *calloc( size_t n, size_t t)
В основном между ними есть два различия:
Поведение:
malloc()
выделяет блок памяти, не инициализируя его, и чтение содержимого этого блока приведет к появлению значений мусора.calloc()
с другой стороны, выделяет блок памяти и инициализирует его нулями, и, очевидно, чтение содержимого этого блока приведет к нулям.Синтаксис:
malloc()
принимает 1 аргумент (размер, который будет выделен), иcalloc()
принимает два аргумента (количество блоков, которые должны быть выделены, и размер каждого блока).
Возвращаемое значение от обоих является указателем на выделенный блок памяти, если успешно. В противном случае будет возвращено значение NULL, указывающее на ошибку выделения памяти.
Пример:
int *arr;
// allocate memory for 10 integers with garbage values
arr = (int *)malloc(10 * sizeof(int));
// allocate memory for 10 integers and sets all of them to 0
arr = (int *)calloc(10, sizeof(int));
Та же функциональность, что и calloc()
может быть достигнуто с помощью malloc()
а также memset()
:
// allocate memory for 10 integers with garbage values
arr= (int *)malloc(10 * sizeof(int));
// set all of them to 0
memset(arr, 0, 10 * sizeof(int));
Обратите внимание, что malloc()
предпочтительно используется поверх calloc()
так как это быстрее Если требуется нулевая инициализация значений, используйте calloc()
вместо.
Разница еще не упомянута: ограничение по размеру
void *malloc(size_t size)
можно выделить только до SIZE_MAX
,
void *calloc(size_t nmemb, size_t size);
может выделить около SIZE_MAX*SIZE_MAX
,
Эта возможность не часто используется на многих платформах с линейной адресацией. Такие системы ограничивают calloc()
с nmemb * size <= SIZE_MAX
,
Рассмотрим тип 512 байтов disk_sector
и код хочет использовать много секторов. Здесь код может использовать только до SIZE_MAX/sizeof disk_sector
сектора.
size_t count = SIZE_MAX/sizeof disk_sector;
disk_sector *p = malloc(count * sizeof *p);
Рассмотрим следующее, которое позволяет еще большее распределение.
size_t count = something_in_the_range(SIZE_MAX/sizeof disk_sector + 1, SIZE_MAX)
disk_sector *p = calloc(count, sizeof *p);
Теперь, если такая система может обеспечить такое большое распределение, это другое дело. Большинство сегодня не будет. И все же это происходило много лет, когда SIZE_MAX
было 65535. Учитывая закон Мура, подозреваю, что это произойдет около 2030 года с некоторыми моделями памяти с SIZE_MAX == 4294967295
и пулы памяти в 100 Гбайт.
Основные различия между malloc и calloc:
- malloc обозначает выделение памяти, тогда как calloc обозначает непрерывное выделение.
malloc принимает только один аргумент, размер блока, тогда как calloc принимает два аргумента, количество блоков, которые должны быть выделены, и размер каждого блока.
ptr = (cast-type *) malloc (byte-size) // malloc
ptr = (тип приведения *)calloc(без блоков, размер блока); // calloc
malloc не выполняет инициализацию памяти, и все адреса хранят значение мусора, тогда как calloc выполняет инициализацию памяти, а адреса инициализируются либо нулевыми, либо нулевыми значениями.
Имя malloc и calloc() - это библиотечные функции, которые динамически распределяют память.
Это означает, что память выделяется во время выполнения (выполнения программы) из сегмента кучи.
Инициализация: malloc () выделяет блок памяти заданного размера (в байтах) и возвращает указатель на начало блока.
> malloc() doesn’t initialize the allocated memory. If we try to access
the content of memory block then we’ll get garbage values. void *
> malloc( size_t size );
> calloc() allocates the memory and also initializes the allocates
memory block to zero. If we try to access the content of these blocks
then we’ll get 0.
> void * calloc( size_t num, size_t size );
Количество аргументов: В отличие от malloc (), calloc() принимает два аргумента: 1) Количество блоков, которые должны быть выделены. 2) Размер каждого блока.
Самое важное:
Было бы лучше использовать malloc вместо calloc, если только мы не хотим инициализации нуля, потому что malloc быстрее, чем calloc. Так что, если мы просто хотим скопировать что-то или сделать что-то, что не требует заполнения блоков нулями, тогда malloc будет лучшим выбором.
malloc(): выделяет запрошенный размер в байтах и возвращает указатель первого байта выделенного пространства
calloc(): выделяет пространство для элементов массива, инициализируется нулями, а затем возвращает указатель на память
Malloc () принимает один аргумент, а calloc () принимает два.
Во-вторых, malloc() не инициализирует выделенную память, а calloc () инициализирует выделенную память нулем. И malloc, и calloc используются в языке C для динамического выделения памяти, они динамически получают блоки памяти.
И то и другое malloc
а также calloc
выделить память, но calloc
инициализирует все биты до нуля, тогда как malloc
нет.
Можно сказать, что Calloc эквивалентен malloc + memset
с 0 (где memset устанавливает указанные биты памяти в ноль).
Поэтому, если в инициализации нулем нет необходимости, использование malloc может быть быстрее.
char *ptr = (char *) malloc (n * sizeof(char));
просто выделяет n bytes
памяти без какой-либо инициализации (т. е. эти байты памяти будут содержать любые значения мусора).
char *ptr = (char *) malloc (n, sizeof(char));
Тем не мение, calloc()
метод в с делает инициализацию как 0
значение для всех занятых байтов памяти в дополнение к функции, которая malloc()
делает.
Но кроме этого, есть очень важное различие. Во время звонка malloc(x)
он выделит память (равно x блокам) и вернет указатель на первый выделенный байт. Однако он не будет проверять, выделено ли ровно x блоков памяти. Это приведет к переполнению памяти. тем не мение calloc()
проверяет размер выделения. Если произойдет сбой при выделении памяти или проверке выделенных байтов, он просто вернет ноль.