В чем разница между выполнением:
ptr = (char **) malloc (MAXELEMS * sizeof(char *));
или:
ptr = (char **) calloc (MAXELEMS, sizeof(char*));
Когда стоит использовать calloc вместо malloc или наоборот?
ptr = calloc(MAXELEMS, sizeof(*ptr));
В чем разница между выполнением:
ptr = (char **) malloc (MAXELEMS * sizeof(char *));
или:
ptr = (char **) calloc (MAXELEMS, sizeof(char*));
Когда стоит использовать calloc вместо malloc или наоборот?
ptr = calloc(MAXELEMS, sizeof(*ptr));
Ответы:
calloc()
дает нулевой инициализированный буфер, а malloc()
память остается неинициализированной.
Для больших выделений большинство calloc
реализаций в основных операционных системах получат страницы с нулевым значением из операционной системы (например, через POSIX mmap(MAP_ANONYMOUS)
или Windows VirtualAlloc
), поэтому им не нужно записывать их в пространстве пользователя. Так обычно malloc
получается больше страниц из ОС; calloc
просто пользуется гарантией ОС.
Это означает, что calloc
память все еще может быть «чистой» и лениво распределенной, а копирование при записи сопоставляется с общесистемной общей физической страницей нулей. (Предполагая систему с виртуальной памятью.)
Некоторые компиляторы даже могут оптимизировать malloc + memset (0) для calloc для вас, но вы должны явно использовать calloc, если хотите, чтобы память читалась как 0
.
Если вы не собираетесь когда-либо читать память перед записью, используйте ее, malloc
чтобы она (потенциально) могла дать вам грязную память из своего внутреннего свободного списка вместо того, чтобы получать новые страницы из ОС. (Или вместо обнуления блока памяти в свободном списке для небольшого выделения).
Внедренные реализации calloc
могут оставить его calloc
себе на ноль памяти, если нет ОС, или это не причудливая многопользовательская ОС, которая обнуляет страницы, чтобы остановить утечки информации между процессами.
В встроенном Linux может использоваться malloc mmap(MAP_UNINITIALIZED|MAP_ANONYMOUS)
, который включен только для некоторых встроенных ядер, поскольку небезопасен в многопользовательской системе.
calloc
не обязательно дороже, так как ОС может сделать некоторые трюки, чтобы ускорить его. Я знаю, что FreeBSD, когда он получает какое-то время простоя ЦП, использует его для запуска простого процесса, который просто обходит и обнуляет освобожденные блоки памяти и помечает блоки, таким образом, процессами с флагом. Поэтому, когда вы это сделаете calloc
, он сначала попытается найти один из таких предварительно обнуленных блоков и просто дать его вам - и, скорее всего, он его найдет.
Менее известное отличие состоит в том, что в операционных системах с оптимистичным распределением памяти, таких как Linux, возвращаемый указатель malloc
не поддерживается реальной памятью до тех пор, пока программа не коснется ее.
calloc
действительно затрагивает память (она записывает нули на нее), и, таким образом, вы будете уверены, что ОС поддерживает выделение с помощью оперативной памяти (или подкачки). По этой же причине он медленнее, чем malloc (он не только обнуляет его, ОС также должна находить подходящую область памяти, возможно, заменяя другие процессы)
См., Например, этот вопрос SO для дальнейшего обсуждения поведения malloc
calloc
не нужно писать нули. Если выделенный блок состоит в основном из новых нулевых страниц, предоставляемых операционной системой, он может оставить их нетронутыми. Это, конечно, требует calloc
настройки операционной системы, а не общей библиотечной функции поверх malloc
. Или разработчик может calloc
сравнить каждое слово с нулем, прежде чем обнулять его. Это не сэкономит время, но предотвратит загрязнение новых страниц.
dlmalloc
подобные реализации пропускают, memset
если чанк был получен с помощью mmap
новых анонимных страниц (или эквивалентных). Обычно этот вид распределения используется для больших кусков, начиная с 256 КБ или около того. Я не знаю ни одной реализации, которая делает сравнение с нулем, прежде чем писать ноль, кроме моей собственной.
omalloc
также пропускает memset
; calloc
не нужно прикасаться к страницам, которые еще не используются приложением (кеш страниц), никогда. Хотя, чрезвычайно примитивные calloc
реализации отличаются.
Одним из часто упускаемых из виду преимуществ 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
. Последний автоматически потерпит неудачу в этом случае, так как большой объект не может быть создан.
Конечно, вам, возможно, придется искать несоответствующие реализации, которые просто игнорируют возможность переполнения ... Если это проблема для платформ, на которые вы ориентируетесь, вам все равно придется выполнить ручной тест на переполнение.
char
является не переполнением, а преобразованием, определяемым реализацией, при присваивании результата обратно в char
объект.
size_t
64-битный, так что это не проблема», это ошибочный способ мышления, который может привести к ошибкам безопасности. size_t
является абстрактным типом, который представляет размеры, и нет никаких оснований считать, что произвольный продукт 32-битного числа и size_t
( sizeof *bar
в принципе, может быть больше 2 ^ 32 в 64-битной реализации C!) подходит size_t
.
Документация делает 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
от вышеупомянутой схемы, состоящей из всех нулей, может представлять собой недопустимое значение, представление прерывания.
Позднее в одном из стандартов Технических исправлений к С99 поведение было определено для всех целочисленных типов (что имеет смысл). Т.е. формально в текущем языке Си вы можете инициализировать только целочисленные типы с calloc
(и memset(..., 0, ...)
). Использование его для инициализации чего-либо еще в общем случае приводит к неопределенному поведению с точки зрения языка Си.
На практике calloc
работает, как мы все знаем :), но хотите ли вы его использовать (учитывая вышеизложенное), решать только вам. Лично я предпочитаю полностью избегать этого, использовать malloc
вместо этого и выполнять свою собственную инициализацию.
Наконец, еще одна важная деталь - это то, что calloc
требуется для внутреннего расчета окончательного размера блока путем умножения размера элемента на количество элементов. При этом calloc
необходимо следить за возможным арифметическим переполнением. Это приведет к неудачному распределению (нулевой указатель), если запрошенный размер блока не может быть правильно рассчитан. Между тем, ваша malloc
версия не пытается следить за переполнением. Он выделит некоторый «непредсказуемый» объем памяти в случае переполнения.
memset(p, v, n * sizeof type);
создает проблему, потому что n * sizeof type
может переполниться. Думаю, мне нужно использовать for(i=0;i<n;i++) p[i]=v;
цикл для надежного кода.
n
элементами, где элемент имеет размер sizeof type
, то он n*sizeof type
не может переполниться, поскольку максимальный размер любого объекта должен быть меньше SIZE_MAX
.
SIZE_MAX
, но здесь нет никаких массивов . Возвращенный указатель calloc()
может указывать на выделенную память, чем превышает SIZE_MAX
. Многие реализации ограничивают произведение двух аргументов calloc()
до SIZE_MAX
, но спецификация C не устанавливает этого ограничения.
из статьи Бенчмаркинг забавы с calloc () и нулевых страниц на блоге Georg Hager в
При выделении памяти с помощью calloc () объем запрошенной памяти выделяется не сразу. Вместо этого все страницы, которые принадлежат блоку памяти, связаны с одной страницей, содержащей все нули, с помощью некоторой магии MMU (ссылки ниже). Если такие страницы только для чтения (что было верно для массивов b, c и d в исходной версии эталонного теста), данные предоставляются с одной нулевой страницы, которая, конечно же, помещается в кэш. Так много для связанных с памятью ядер циклов. Если страница записывается (независимо от того, как), происходит сбой, отображается «настоящая» страница, а нулевая страница копируется в память. Это называется копированием при записи, хорошо известным подходом к оптимизации (который я даже несколько раз преподавал в своих лекциях по C ++). После того,
calloc
как правило, malloc+memset
до 0
Обычно немного лучше использовать malloc+memset
явно, особенно когда вы делаете что-то вроде:
ptr=malloc(sizeof(Item));
memset(ptr, 0, sizeof(Item));
Это лучше, потому что sizeof(Item)
это известно компилятору во время компиляции, и компилятор в большинстве случаев заменит его наилучшими инструкциями для обнуления памяти. С другой стороны, если memset
происходит в calloc
, размер параметра выделения не скомпилирован в calloc
коде, и memset
часто вызывается real , который обычно содержит код, который выполняет побайтовое заполнение до длинной границы, чем цикл для заполнения sizeof(long)
заполнение памяти кусками и, наконец, побайтовое заполнение оставшегося пространства. Даже если распределитель достаточно умен, чтобы вызывать aligned_memset
его, он все равно будет универсальным циклом.
Одно заметное исключение может произойти, когда вы выполняете malloc / calloc для очень большого куска памяти (некоторые power_of_two килобайта), в этом случае выделение может быть сделано непосредственно из ядра. Поскольку ядра ОС обычно обнуляют всю память, которую они отдают по соображениям безопасности, достаточно умный calloc может просто вернуть ее с дополнительным обнулением. Опять же - если вы просто выделяете что-то, что, как вы знаете, мало, вам может быть лучше с malloc + memset с точки зрения производительности.
calloc()
медленнее, чем malloc()
: умножение на размер. calloc()
требуется использовать общее умножение (если size_t
это 64-битные, даже очень дорогие операции 64-битные * 64-битные = 64-битные), тогда как malloc () часто будет иметь постоянную времени компиляции.
struct foo { char a,b,c; };
. calloc
всегда лучше, чем malloc
+ memset
, если вы всегда собираетесь очистить весь malloc
регион ed. calloc
имеет тщательную, но эффективную проверку на переполнение int в элементах размера *.
Разница 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()
принимает один аргумент (требуется память в байтах), а 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()
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 Гбайт.
size_t
более 32 бит. Единственный вопрос заключается в том calloc
, SIZE_MAX
можно ли полагаться на использование со значениями, чей продукт превышает, чтобы получить ноль, а не возвращать указатель на меньшее распределение.
calloc()
превышение распределения SIZE_MAX
. Это случалось в прошлом с 16-разрядной size_t
версией, и, поскольку память продолжает дешеветь, я не вижу причин, по которым это не может произойти, даже если это не распространено .
SIZE_MAX
. Это, конечно, не требует наличия каких-либо обстоятельств, при которых такое распределение может быть успешным; Я не уверен, что есть какая-то особая выгода от того, что реализация, которая не может обрабатывать такое распределение, должна возвращаться NULL
(особенно если учесть, что в некоторых реализациях обычно есть malloc
указатели возврата в пространство, которое еще не зафиксировано и может быть недоступно, когда код фактически пытается использовать). Это).
size_t
в uint64_t
?
Количество блоков:
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 означает «непрерывное распределение».
malloc