В чем разница между выполнением:
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_t64-битный, так что это не проблема», это ошибочный способ мышления, который может привести к ошибкам безопасности. 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