Вопрос по calloc, malloc, c – Разница между malloc и calloc?

703

В чем разница между выполнением:

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

или же:

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

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

В C вы можете написать выше в более общем виде, как:ptr = calloc(MAXELEMS, sizeof(*ptr)); chqrlie
Интересный пост о разнице между calloc и malloc + memsetvorpus.org/blog/why-does-calloc-exist ddddavidee
@ddddavidee Я тоже нашел этот блог после того, как был недоволен таким количеством ответов в сети. Натаниэль Дж. Смит заслуживает более 100 баллов за свой анализ. lifebalance

Ваш Ответ

12   ответов
5

ограничение размера

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 Гбайт.

Стандарт C позволяет кодузапрос распределение, размер которого превышаетSIZE_MAX, Это, конечно, не требует наличия каких-либо обстоятельств, при которых такое распределение может быть успешным; Я не уверен, что есть какая-то особая выгода от того, что реализации, которые не могут обрабатывать такие распределения, должны возвращатьNULL (особенно если учесть, что для некоторых реализаций характерноmalloc вернуть указатели в пространство, которое еще не зафиксировано и может быть недоступно, когда код на самом деле пытается его использовать). supercat
Кроме того, там, где в прошлом могли существовать системы, доступный диапазон адресов которых превышал наибольшее представимое целое число, я не вижу реальной возможности того, чтобы это когда-либо происходило снова, поскольку для этого потребовалась бы емкость в миллиарды гигабайт. Даже если бы закон Мура продолжал соблюдаться, переход от точки, в которой 32 бита перестало быть достаточным, к точке, в которой 64 бита перестало быть достаточным, потребовал бы вдвое больше времени, чем переход от точки, в которой 16 бит было достаточно, к точке, где 32 бита не было. «т. supercat
Согласен о вашемобобщениеТем не менее, спецификация C позволяетcalloc() ассигнования, превышающиеSIZE_MAX, Это случилось в прошлом с 16-битнымsize_t и поскольку память продолжает дешеветь, я не вижу причин, по которым это не может произойти, даже если это не произойдетобщий. chux
Почему реализация, которая может вместить одно выделение сверх 4G, не определяетsize_t вuint64_t? supercat
Обычно size_t может содержать размер самого большого объекта, который может обработать программа. Система, в которой size_t равен 32 битам, вряд ли сможет обрабатывать выделение, превышающее 4294967295 байт, и систему, которая сможет обрабатывать выделения, размер которых почти наверняка составитsize_t больше 32 бит. Единственный вопрос, является ли использованиеcalloc со значениями, чей продукт превышаетSIZE_MAX можно полагаться, чтобы получить ноль, а не возвращать указатель на меньшее выделение. supercat
6

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

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

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() вместо.

7


Во-первых, это количество аргументов.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() не.

11

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 с точки зрения производительности.

У glibc calloc есть некоторые умения, чтобы решить, как наиболее эффективно очистить возвращенный кусок, например, иногда требуется очистка только его части, а также развернутой очистки до 9 * sizeof (size_t). Память - это память, очистка ее 3 байта за раз не будет быстрее только потому, что вы собираетесь использовать ее для храненияstruct foo { char a,b,c; };. calloc всегда лучше чемmalloc+memset, если вы всегда собираетесь очистить весьmallocЭд регион.calloc имеет тщательную, но эффективную проверку на переполнение int в элементах размера *. Peter Cordes
Есть также второй момент, который делаетcalloc() медленнее чемmalloc(): умножение на размер.calloc() требуется использовать общее умножение (еслиsize_t равняется 64 битам, даже очень дорогостоящим операциям 64 бит * 64 бит = 64 бит), тогда как malloc () часто будет иметь постоянную времени компиляции. Patrick Schlüter
+1 за напоминание о том, что общая реализация функциональности в системной библиотеке не обязательно быстрее, чем та же операция в пользовательском коде. Patrick Schlüter
32

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

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

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

102

Одно часто пропускаемое преимущество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, Последний автоматически потерпит неудачу в этом случае, так как большой объект не может быть создан.

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

По-видимому, именно арифметическое переполнение стало причиной дыры в OpenSSH в 2002 году. Хорошая статья из OpenBSD об опасностях этого с функциями, связанными с памятью:undeadly.org/cgi?action=article&sid=20060330071917 Komrade P.
@tristopia: Дело не в том, что код можно использовать во всех реализациях, а в том, что он некорректен без дополнительных предположений и, следовательно, не корректно / переносимо. R..
Это возможно только для иллюстрации. Потому что компилятор все равно может оптимизировать это. Мой компилируется в этот ассм: push 1. Komrade P.
@tristopia: если ваш образ мышления "size_t 64-битный, так что это не проблема ", это ошибочный способ мышления, который может привести к ошибкам в безопасности.size_t является абстрактным типом, который представляет размеры, и нет никаких оснований думать о произвольном произведении 32-битного числа иsize_t (нота:sizeof *bar в принципе может быть больше 2 ^ 32 в 64-битной реализации C!)size_t. R..
@ KomradeP .: Интересно. К сожалению, статья, на которую вы ссылаетесь, имеет дезинформацию в самом начале. Пример сchar являетсяне переполнение, а скорее преобразование, определяемое реализацией, при присваивании результата обратно вchar объект. R..
8

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 *, если они не приводятся по типу.!

И почему вы сохраняете data_type и cast_type разными? Sold Out
20

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

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

Первая строка ответа содержит ссылку на блог Георга Хагера. Ashish Chavan
где ссылка? Rupesh Yadav.
343

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

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

Смотри напримерэтот ТАК вопрос для дальнейшего обсуждения поведения malloc

omalloc также пропускаетmemset; calloc не нужно прикасаться к страницам, которые еще не используются приложением (кеш страниц), никогда. Хотя,чрезвычайно примитивноcalloc реализации отличаются. mirabilos
Всеdlmallocподобные реализации пропуститьmemset если кусок был получен черезmmapдобавление новых анонимных страниц (или эквивалент). Обычно этот вид распределения используется для больших кусков, начиная с 256 КБ или около того. Я не знаю ни одной реализации, которая делает сравнение с нулем, прежде чем писать ноль, кроме моей собственной. R..
@R .. интересная заметка. Но на практике, такие реализации существуют в дикой природе? Isak Savo
Calloc в glibc проверяет, получает ли она свежую память от ОС. Если это так, он знает, что не нужно его писать, потому что mmap (..., MAP_ANONYMOUS) возвращает память, которая уже обнулена. Peter Cordes
calloc не нужно писать нули. Если выделенный блок состоит в основном из новых нулевых страниц, предоставляемых операционной системой, он может оставить их нетронутыми. Это конечно требуетcalloc быть настроенным на операционную систему, а не на общую библиотечную функцию поверхmalloc, Или разработчик может сделатьcalloc сравните каждое слово с нулем, прежде чем обнулить его. Это не сэкономит время, но предотвратит загрязнение новых страниц. R..
781

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

РЕДАКТИРОВАТЬ:

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

Я склонен считать, что если ваш код становится «более безопасным» в результате нулевого выделения ресурсов по умолчанию, то ваш код недостаточно безопасен, независимо от того, используете ли вы malloc или calloc. Использование malloc является хорошим индикатором того, что данные должны быть инициализированы - я использую calloc только в тех случаях, когда эти 0 байтов действительно значимы. Также обратите внимание, что calloc не обязательно делает то, что вы думаете для не-char типов. Никто на самом деле больше не использует представления ловушек или плавающие не-IEEE, но это не повод думать, что ваш код действительно переносим, когда это не так. Steve Jessop
calloc не обязательно дороже, так как ОС может сделать некоторые трюки, чтобы ускорить его. Я знаю, что FreeBSD, когда он получает какое-то время простоя ЦП, использует его для запуска простого процесса, который просто обходит и обнуляет освобожденные блоки памяти и помечает блоки, таким образом, процессами с флагом. Итак, когда вы делаетеcallocСначала он пытается найти один из таких предварительно обнуленных блоков и просто дать его вам - и, скорее всего, он его найдет. Pavel Minaev
Используйте malloc (), если вы собираетесь установить все, что вы используете в выделенном пространстве. Используйте calloc (), если вы собираетесь оставить части данных неинициализированными, и было бы полезно обнулить ненастроенные части. Jonathan Leffler
Варианты * alloc являются довольно мнемоническими - clear-alloc, memory-alloc, re-alloc. Cascabel
@SteveJessop "Безопаснее" не правильное слово. Я думаю, что «детерминист» - лучший термин. Код, который является более детерминированным, чем наличие сбоев, зависящих от синхронизации и последовательности данных, будет легче изолировать сбои. Calloc - иногда простой способ получить этот детерминизм по сравнению с явной инициализацией. dennis
26

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

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

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

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

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

Было бы полезно, если бы существовали стандартные средства, с помощью которых код мог бы утверждать, что реализация должна использовать все биты-ноль в качестве нулевого указателя (в противном случае отказывается от компиляции), поскольку существуют реализации, которые используют другие представления нулевого указателя, но они сравнительно редко; код, который не должен выполняться в таких реализациях, может быть быстрее, если он может использовать calloc () или memset для инициализации массивов указателей. supercat
vorpus.org/blog/why-does-calloc-exist ddddavidee
@chux Нет, если массив сn элементы существуют там, где элемент имеет размерsizeof type, тогдаn*sizeof type не может переполниться, потому что максимальный размер любого объекта должен быть меньшеSIZE_MAX. 12431234123412341234123
Согласно пункту «еще одна важная деталь»:memset(p, v, n * sizeof type); проблема, потому чтоn * sizeof type может переполниться Думаю, мне нужно использоватьfor(i=0;i<n;i++) p[i]=v; цикл для надежного кода. chux
@ 12431234123412341234123 Правда омассив размер <=SIZE_MAXпока нетмассивы Вот. Указатель возвращается изcalloc() может указывать на выделенную память, чем превышаетSIZE_MAX, Многие реализации ограничивают произведение двух аргументовcalloc() вSIZE_MAX, тем не менее, спецификация C не устанавливает этот предел. chux

Похожие вопросы