@ddddavidee Я тоже нашел этот блог после того, как был недоволен таким количеством ответов в сети. Натаниэль Дж. Смит заслуживает более 100 баллов за свой анализ.
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), который включен только для некоторых встроенных ядер, поскольку небезопасен в многопользовательской системе.
Варианты * alloc являются довольно мнемоническими - clear-alloc, memory-alloc, re-alloc.
Каскабель
43
Используйте malloc (), если вы собираетесь установить все, что вы используете в выделенном пространстве. Используйте calloc (), если вы собираетесь оставить части данных неинициализированными, и было бы полезно обнулить ненастроенные части.
Джонатан Леффлер
268
callocне обязательно дороже, так как ОС может сделать некоторые трюки, чтобы ускорить его. Я знаю, что FreeBSD, когда он получает какое-то время простоя ЦП, использует его для запуска простого процесса, который просто обходит и обнуляет освобожденные блоки памяти и помечает блоки, таким образом, процессами с флагом. Поэтому, когда вы это сделаете calloc, он сначала попытается найти один из таких предварительно обнуленных блоков и просто дать его вам - и, скорее всего, он его найдет.
Павел Минаев
28
Я склонен считать, что если ваш код становится «более безопасным» в результате нулевого выделения ресурсов по умолчанию, то ваш код недостаточно безопасен, независимо от того, используете ли вы malloc или calloc. Использование malloc является хорошим индикатором того, что данные должны быть инициализированы - я использую calloc только в тех случаях, когда эти 0 байтов действительно значимы. Также обратите внимание, что calloc не обязательно делает то, что вы думаете для не-char типов. Никто на самом деле больше не использует представления ловушек или плавающие не-IEEE, но это не повод думать, что ваш код действительно переносим, когда это не так.
Стив Джессоп
18
@SteveJessop "Безопаснее" не правильное слово. Я думаю, что «детерминист» - лучший термин. Код, который является более детерминированным, чем наличие сбоев, зависящих от синхронизации и последовательности данных, будет легче изолировать сбои. Calloc - иногда простой способ получить этот детерминизм по сравнению с явной инициализацией.
Деннис
362
Менее известное отличие состоит в том, что в операционных системах с оптимистичным распределением памяти, таких как Linux, возвращаемый указатель mallocне поддерживается реальной памятью до тех пор, пока программа не коснется ее.
callocдействительно затрагивает память (она записывает нули на нее), и, таким образом, вы будете уверены, что ОС поддерживает выделение с помощью оперативной памяти (или подкачки). По этой же причине он медленнее, чем malloc (он не только обнуляет его, ОС также должна находить подходящую область памяти, возможно, заменяя другие процессы)
См., Например, этот вопрос SO для дальнейшего обсуждения поведения malloc
callocне нужно писать нули. Если выделенный блок состоит в основном из новых нулевых страниц, предоставляемых операционной системой, он может оставить их нетронутыми. Это, конечно, требует callocнастройки операционной системы, а не общей библиотечной функции поверх malloc. Или разработчик может callocсравнить каждое слово с нулем, прежде чем обнулять его. Это не сэкономит время, но предотвратит загрязнение новых страниц.
R .. GitHub ОСТАНОВИТЬ ЛЬДА
3
@R .. интересная заметка. Но на практике, такие реализации существуют в дикой природе?
Исак Саво
10
Все dlmallocподобные реализации пропускают, memsetесли чанк был получен с помощью mmapновых анонимных страниц (или эквивалентных). Обычно этот вид распределения используется для больших кусков, начиная с 256 КБ или около того. Я не знаю ни одной реализации, которая делает сравнение с нулем, прежде чем писать ноль, кроме моей собственной.
R .. GitHub ОСТАНОВИТЬ ЛЬДА
1
omallocтакже пропускает memset; callocне нужно прикасаться к страницам, которые еще не используются приложением (кеш страниц), никогда. Хотя, чрезвычайно примитивные callocреализации отличаются.
Мирабилось
10
Calloc в glibc проверяет, получает ли она свежую память от ОС. Если это так, он знает, что не нужно его писать, потому что mmap (..., MAP_ANONYMOUS) возвращает память, которая уже обнулена.
Питер Кордес
112
Одним из часто упускаемых из виду преимуществ callocявляется то, что (совместимые реализации) это поможет защитить вас от целочисленных уязвимостей переполнения. Для сравнения:
Первое может привести к крошечному выделению и последующим переполнениям буфера, если countоно больше, чем SIZE_MAX/sizeof *bar. Последний автоматически потерпит неудачу в этом случае, так как большой объект не может быть создан.
Конечно, вам, возможно, придется искать несоответствующие реализации, которые просто игнорируют возможность переполнения ... Если это проблема для платформ, на которые вы ориентируетесь, вам все равно придется выполнить ручной тест на переполнение.
По-видимому, именно арифметическое переполнение стало причиной дыры в OpenSSH в 2002 году. Хорошая статья из OpenBSD об опасностях этого с функциями, связанными с памятью: undeadly.org/cgi?action=article&sid=20060330071917
Филипп П.
4
@ KomradeP .: Интересно. К сожалению, статья, на которую вы ссылаетесь, имеет дезинформацию в самом начале. Пример с charявляется не переполнением, а преобразованием, определяемым реализацией, при присваивании результата обратно в charобъект.
R .. GitHub ОСТАНОВИТЬСЯ, ПОМОГАЯ ЛЬДУ
Это возможно только для иллюстрации. Потому что компилятор все равно может оптимизировать это. Мой компилируется в этот ассм: push 1.
Филипп П.
1
@tristopia: Дело не в том, что код можно использовать во всех реализациях, а в том, что он некорректен без дополнительных предположений и, следовательно, не корректно / переносимо.
R .. GitHub ОСТАНОВИТЬСЯ, ПОМОГАЯ ЛЬДУ
3
@tristopia: Если ваш образ мышления « size_t64-битный, так что это не проблема», это ошибочный способ мышления, который может привести к ошибкам безопасности. size_tявляется абстрактным типом, который представляет размеры, и нет никаких оснований считать, что произвольный продукт 32-битного числа и size_t( sizeof *barв принципе, может быть больше 2 ^ 32 в 64-битной реализации C!) подходит size_t.
R .. GitHub ОСТАНОВИТЬСЯ, ПОМОГАЯ ЛЕДОМ
37
Документация делает calloc похожим на malloc, который просто инициализирует память нулем; это не основная разница! Идея calloc состоит в том, чтобы избежать семантики копирования при записи для выделения памяти. Когда вы выделяете память с помощью calloc, все это отображается на одной физической странице, которая инициализируется нулем. Когда любая из страниц выделенной памяти записывается в физическую страницу, выделяется. Это часто используется для создания ОГРОМНЫХ хеш-таблиц, например, поскольку пустые части хеш-функции не поддерживаются какой-либо дополнительной памятью (страницами); они с радостью указывают на единственную инициализированную нулями страницу, которая может быть даже разделена между процессами.
Любая запись в виртуальный адрес сопоставляется со страницей, если эта страница является нулевой страницей, выделяется другая физическая страница, там копируется нулевая страница и поток управления возвращается клиентскому процессу. Это работает так же, как файлы, отображенные в память, виртуальная память и т. Д. Работают ... он использует пейджинг.
Там нет разницы в размере выделенного блока памяти. 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;цикл для надежного кода.
chux - Восстановить Монику
Было бы полезно, если бы существовали стандартные средства, с помощью которых код мог бы утверждать, что реализация должна использовать все биты-ноль в качестве нулевого указателя (иначе отказывается от компиляции), поскольку существуют реализации, которые используют другие представления нулевого указателя, но они сравнительно редко; код, который не должен выполняться в таких реализациях, может быть быстрее, если он может использовать calloc () или memset для инициализации массивов указателей.
суперкат
@chux Нет, если существует массив с nэлементами, где элемент имеет размер sizeof type, то он n*sizeof typeне может переполниться, поскольку максимальный размер любого объекта должен быть меньше SIZE_MAX.
12431234123412341234123
@ 12431234123412341234123 Правда о размере массива <= SIZE_MAX, но здесь нет никаких массивов . Возвращенный указатель calloc()может указывать на выделенную память, чем превышает SIZE_MAX. Многие реализации ограничивают произведение двух аргументов calloc()до SIZE_MAX, но спецификация C не устанавливает этого ограничения.
При выделении памяти с помощью calloc () объем запрошенной памяти выделяется не сразу. Вместо этого все страницы, которые принадлежат блоку памяти, связаны с одной страницей, содержащей все нули, с помощью некоторой магии MMU (ссылки ниже). Если такие страницы только для чтения (что было верно для массивов b, c и d в исходной версии эталонного теста), данные предоставляются с одной нулевой страницы, которая, конечно же, помещается в кэш. Так много для связанных с памятью ядер циклов. Если страница записывается (независимо от того, как), происходит сбой, отображается «настоящая» страница, а нулевая страница копируется в память. Это называется копированием при записи, хорошо известным подходом к оптимизации (который я даже несколько раз преподавал в своих лекциях по C ++). После того,
Это лучше, потому что sizeof(Item)это известно компилятору во время компиляции, и компилятор в большинстве случаев заменит его наилучшими инструкциями для обнуления памяти. С другой стороны, если memsetпроисходит в calloc, размер параметра выделения не скомпилирован в callocкоде, и memsetчасто вызывается real , который обычно содержит код, который выполняет побайтовое заполнение до длинной границы, чем цикл для заполнения sizeof(long)заполнение памяти кусками и, наконец, побайтовое заполнение оставшегося пространства. Даже если распределитель достаточно умен, чтобы вызывать aligned_memsetего, он все равно будет универсальным циклом.
Одно заметное исключение может произойти, когда вы выполняете malloc / calloc для очень большого куска памяти (некоторые power_of_two килобайта), в этом случае выделение может быть сделано непосредственно из ядра. Поскольку ядра ОС обычно обнуляют всю память, которую они отдают по соображениям безопасности, достаточно умный calloc может просто вернуть ее с дополнительным обнулением. Опять же - если вы просто выделяете что-то, что, как вы знаете, мало, вам может быть лучше с malloc + memset с точки зрения производительности.
+1 за напоминание о том, что общая реализация функциональности в системной библиотеке не обязательно быстрее, чем та же операция в пользовательском коде.
Патрик Шлютер
1
Есть также второй момент, который делает calloc()медленнее, чем malloc(): умножение на размер. calloc()требуется использовать общее умножение (если size_tэто 64-битные, даже очень дорогие операции 64-битные * 64-битные = 64-битные), тогда как malloc () часто будет иметь постоянную времени компиляции.
Патрик Шлютер
4
У glibc calloc есть некоторые умения, чтобы решить, как наиболее эффективно очистить возвращенный кусок, например, иногда только его часть нуждается в очистке, а также развернутый очистить до 9 * sizeof (size_t). Память - это память, очистка ее 3 байта за раз не будет быстрее только потому, что вы собираетесь использовать ее для хранения struct foo { char a,b,c; };. callocвсегда лучше, чем malloc+ memset, если вы всегда собираетесь очистить весь mallocрегион ed. callocимеет тщательную, но эффективную проверку на переполнение int в элементах размера *.
Питер Кордес
8
Разница 1:
malloc() Обычно выделяется блок памяти и инициализируется сегмент памяти.
calloc() выделяет блок памяти и инициализирует весь блок памяти в 0.
Разница 2:
Если вы учитываете malloc()синтаксис, он будет принимать только 1 аргумент. Рассмотрим следующий пример ниже:
И почему вы сохраняете data_type и cast_type разными?
Распродано
7
Есть два отличия.
Во-первых, это количество аргументов. malloc()принимает один аргумент (требуется память в байтах), а calloc()требуется два аргумента.
Во-вторых, malloc()не инициализирует выделенную память, а calloc()инициализирует выделенную память нулем.
calloc()выделяет область памяти, длина будет равна произведению ее параметров. callocзаполняет память нулями и возвращает указатель на первый байт. Если ему не удается найти достаточно места, он возвращает NULLуказатель.
malloc()выделяет один блок памяти REQUSTED SIZE и возвращает указатель на первый байт. Если он не может найти требуемое количество памяти, он возвращает нулевой указатель.
Синтаксис: функция принимать один аргумент, который является количеством байт для распределения, в то время как функция принимает два аргумента, один из которых числа элементов, а другое количество байт для распределения для каждого из этих элементов. Также инициализирует выделенное пространство нулями, пока нет.ptr_var=(cast_type *)malloc(Size_in_bytes);malloc()calloc()calloc()malloc()
malloc()и calloc()являются функциями из стандартной библиотеки C, которые позволяют динамическое выделение памяти, то есть оба они позволяют выделять память во время выполнения.
Поведение: 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_MAXбыло 65535. Учитывая закон Мура , подозреваю, что это произойдет около 2030 года с некоторыми моделями SIZE_MAX == 4294967295памяти с пулами памяти в 100 Гбайт.
Как правило, size_t может содержать размер самого большого объекта, который может обработать программа. Система, в которой size_t равен 32 битам, вряд ли сможет обрабатывать выделение, превышающее 4294967295 байт, и систему, которая будет в состоянии обрабатывать выделения, размер которых почти наверняка составит size_tболее 32 бит. Единственный вопрос заключается в том calloc, SIZE_MAXможно ли полагаться на использование со значениями, чей продукт превышает, чтобы получить ноль, а не возвращать указатель на меньшее распределение.
суперкат
Согласитесь с вашим обобщением , но спецификация C допускает calloc()превышение распределения SIZE_MAX. Это случалось в прошлом с 16-разрядной size_tверсией, и, поскольку память продолжает дешеветь, я не вижу причин, по которым это не может произойти, даже если это не распространено .
Чукс - Восстановить Монику
1
Стандарт C позволяет коду запрашивать распределение, размер которого превышает SIZE_MAX. Это, конечно, не требует наличия каких-либо обстоятельств, при которых такое распределение может быть успешным; Я не уверен, что есть какая-то особая выгода от того, что реализация, которая не может обрабатывать такое распределение, должна возвращаться NULL(особенно если учесть, что в некоторых реализациях обычно есть mallocуказатели возврата в пространство, которое еще не зафиксировано и может быть недоступно, когда код фактически пытается использовать). Это).
суперкат
Кроме того, там, где в прошлом могли существовать системы, у которых доступный диапазон адресов превышал наибольшее представимое целое число, я не вижу реальной возможности того, чтобы это когда-либо происходило снова, поскольку для этого потребовалась бы емкость в миллиарды гигабайт. Даже если бы закон Мура продолжал выполняться, переход от точки, в которой 32 бита перестало быть достаточным, к точке, в которой 64 бита перестало быть достаточным, потребовал бы вдвое больше времени, чем переход от точки, в которой 16 бит было достаточно, к точке, где 32 бита не было. «т.
суперкат
1
Почему реализация которых может вместить один выделение в избытке 4G не определяют size_tв uint64_t?
суперкат
2
Количество блоков:
malloc () назначает один блок запрошенной памяти,
calloc () назначает несколько блоков запрошенной памяти
Инициализация:
malloc () - не очищает и не инициализирует выделенную память.
calloc () - инициализирует выделенную память нулем.
Скорость:
malloc () быстро.
calloc () медленнее, чем malloc ().
Аргументы и синтаксис:
malloc () принимает 1 аргумент:
байтов
Количество байтов для выделения
calloc () принимает 2 аргумента:
длина
количество блоков памяти, которые будут выделены
байтов
количество байтов, выделяемых в каждом блоке памяти
Способ распределения памяти:
функция malloc назначает память нужного «размера» из доступной кучи.
Функция calloc назначает память, размер которой равен num * size.
Значение для имени:
имя malloc означает «распределение памяти».
Название calloc означает «непрерывное распределение».
malloc
ptr = calloc(MAXELEMS, sizeof(*ptr));
Ответы:
calloc()
дает нулевой инициализированный буфер, аmalloc()
память остается неинициализированной.Для больших выделений большинство
calloc
реализаций в основных операционных системах получат страницы с нулевым значением из операционной системы (например, через POSIXmmap(MAP_ANONYMOUS)
или WindowsVirtualAlloc
), поэтому им не нужно записывать их в пространстве пользователя. Так обычно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
является то, что (совместимые реализации) это поможет защитить вас от целочисленных уязвимостей переполнения. Для сравнения:против
Первое может привести к крошечному выделению и последующим переполнениям буфера, если
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
как правило,malloc+memset
до 0Обычно немного лучше использовать
malloc+memset
явно, особенно когда вы делаете что-то вроде:Это лучше, потому что
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 аргумент. Рассмотрим следующий пример ниже:Пример: если вы хотите выделить 10 блоков памяти для типа int,
Если вы считаете
calloc()
синтаксис, он будет принимать 2 аргумента. Рассмотрим следующий пример ниже:Пример: если вы хотите выделить 10 блоков памяти для типа int и инициализировать все это в ZERO,
Сходство:
Оба
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, которые позволяют динамическое выделение памяти, то есть оба они позволяют выделять память во время выполнения.Их прототипы следующие:
Между ними в основном два различия:
Поведение:
malloc()
выделяет блок памяти без его инициализации, и чтение содержимого этого блока приведет к появлению значений мусора.calloc()
с другой стороны, выделяет блок памяти и инициализирует его нулями, и, очевидно, чтение содержимого этого блока приведет к нулям.Синтаксис:
malloc()
принимает 1 аргумент (размер, который должен быть выделен) иcalloc()
принимает два аргумента (количество блоков, которые должны быть выделены, и размер каждого блока).Возвращаемое значение от обоих является указателем на выделенный блок памяти, если успешно. В противном случае будет возвращено значение NULL , указывающее на ошибку выделения памяти.
Пример:
Та же функциональность,
calloc()
которую можно достичь с помощьюmalloc()
иmemset()
:Обратите внимание, что
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_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 аргумента:
длина
Способ распределения памяти:
функция malloc назначает память нужного «размера» из доступной кучи.
Функция calloc назначает память, размер которой равен num * size.
Значение для имени:
имя malloc означает «распределение памяти».
Название calloc означает «непрерывное распределение».
источник