Разница между malloc и calloc?

620

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

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

или

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

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

Показать ещё 3 комментария
Теги:
malloc
calloc

18 ответов

772
Лучший ответ

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

EDIT:

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

  • 210
    Варианты * alloc являются довольно мнемоническими - clear-alloc, memory-alloc, re-alloc.
  • 40
    Используйте malloc (), если вы собираетесь установить все, что вы используете в выделенном пространстве. Используйте calloc (), если вы собираетесь оставить части данных неинициализированными, и было бы полезно обнулить ненастроенные части.
Показать ещё 20 комментариев
341

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

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

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

  • 44
    calloc не нужно писать нули. Если выделенный блок состоит в основном из новых нулевых страниц, предоставляемых операционной системой, он может оставить их нетронутыми. Это, конечно, требует, чтобы calloc был настроен на операционную систему, а не на общую библиотечную функцию поверх malloc . Или разработчик может заставить calloc сравнивать каждое слово с нулем, прежде чем обнулять его. Это не сэкономит время, но предотвратит загрязнение новых страниц.
  • 3
    @R .. интересная заметка. Но на практике, такие реализации существуют в дикой природе?
Показать ещё 5 комментариев
99

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

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

  • 17
    По-видимому, именно арифметическое переполнение стало причиной дыры в OpenSSH в 2002 году. Хорошая статья от OpenBSD об опасностях этого с функциями, связанными с памятью: undeadly.org/cgi?action=article&sid=20060330071917
  • 4
    @ KomradeP .: Интересно. К сожалению, статья, на которую вы ссылаетесь, имеет дезинформацию в самом начале. Пример с char - это не переполнение, а преобразование, определяемое реализацией, при присваивании результата обратно в объект char .
Показать ещё 6 комментариев
28

Документация делает calloc похожим на malloc, который просто выполняет нулевую инициализацию памяти; это не главное отличие! Идея calloc состоит в том, чтобы абстрагировать семантику copy-on-write для выделения памяти. Когда вы выделяете память с помощью calloc, она сопоставляется с той же самой физической страницей, которая инициализируется нулем. Когда выделяется какая-либо из страниц выделенной памяти на физическую страницу. Это часто используется для создания HUGE хэш-таблиц, например, поскольку части хэша, которые пусты, не поддерживаются дополнительной памятью (страницами); они с радостью указывают на единственную нулевую инициализацию страницы, которая может быть разделена между процессами.

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

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

24

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

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

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

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

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

  • 0
    Согласно параграфу «еще одна важная деталь»: что делает make для memset(p, v, n * sizeof type); проблема, потому что n * sizeof type может переполниться. Думаю, мне нужно использовать for(i=0;i<n;i++) p[i]=v; цикл для надежного кода.
  • 0
    Было бы полезно, если бы существовали стандартные средства, с помощью которых код мог бы утверждать, что реализация должна использовать все биты-ноль в качестве нулевого указателя (в противном случае отказывается от компиляции), поскольку существуют реализации, которые используют другие представления нулевого указателя, но они сравнительно редко; код, который не должен выполняться в таких реализациях, может быть быстрее, если он может использовать calloc () или memset для инициализации массивов указателей.
Показать ещё 3 комментария
17

из статьи Бенчмаркинг с помощью calloc() и нулевых страниц в Блог Georg Hager

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

  • 0
    где ссылка?
  • 2
    Первая строка ответа содержит ссылку на блог Георга Хагера.
10

calloc обычно malloc+memset до 0

Обычно лучше использовать malloc+memset явно, особенно когда вы делаете что-то вроде:

ptr=malloc(sizeof(Item));
memset(ptr, 0, sizeof(Item));

Это лучше, потому что sizeof(Item) знает компилятор во время компиляции, и компилятор в большинстве случаев заменяет его наилучшими возможными инструкциями для нулевой памяти. С другой стороны, если memset происходит в calloc, размер параметра распределения не компилируется в коде calloc, и часто вызывается реальный memset, который обычно содержит код для байтов -byte заполнить до длинной границы, чем цикл, чтобы заполнить память в sizeof(long) кусках и, наконец, побайтовое заполнение оставшегося пространства. Даже если распределитель достаточно умен, чтобы вызвать некоторый aligned_memset, он все равно будет общим циклом.

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

  • 0
    +1 за напоминание о том, что общая реализация функциональности в системной библиотеке не обязательно быстрее, чем та же операция в пользовательском коде.
  • 1
    Есть также вторая точка, которая делает calloc() медленнее, чем malloc() : умножение на размер. calloc() требуется для использования общего умножения (если size_t равен 64 битам, даже очень дорогая операция 64 бит * 64 бит = 64 бит), тогда как malloc () часто будет иметь постоянную времени компиляции.
Показать ещё 1 комментарий
9

malloc() выделяет блок памяти заданного размера (в байтах) и возвращает указатель на начало блока.

void *malloc(size_t size);

malloc() не инициализирует выделенную память.

calloc() выделяет память, а также инициализирует выделение памяти для всех битов 0.

void *calloc(size_t num, size_t size);
7

Разница 1: malloc() обычно выделяет блок памяти, и это инициализированный сегмент памяти. calloc() выделяет блок памяти и инициализирует весь блок памяти до 0.

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

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 * по умолчанию, если они не являются литыми.!

  • 0
    И почему вы сохраняете data_type и cast_type разными?
6

Есть два отличия.
Во-первых, это число аргументов. malloc() принимает один аргумент (требуется память в байтах), а calloc() - два аргумента.
Во-вторых, malloc() не инициализирует выделенную память, а calloc() инициализирует выделенную память ZERO.

  • calloc() выделяет область памяти, длина будет продуктом ее параметров. calloc заполняет память с помощью ZERO и возвращает указатель на первый байт. Если ему не удается найти достаточное пространство, он возвращает указатель 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() - нет.

5

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

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

Разница пока не указана: ограничение размера

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 GBytes.

  • 2
    Обычно size_t может содержать размер самого большого объекта, который может обработать программа. Система, в которой size_t равен 32 битам, вряд ли сможет обрабатывать выделение, превышающее 4294967295 байт, и систему, которая сможет обрабатывать выделения такого размера, почти наверняка сделает size_t больше 32 бит. Единственный вопрос заключается в том, можно ли полагаться на использование calloc со значениями, произведение которых превышает SIZE_MAX чтобы получить ноль, а не возвращать указатель на меньшее распределение.
  • 0
    Согласитесь с вашим обобщением , но спецификация C допускает, что выделения calloc() превышают SIZE_MAX . Это происходило в прошлом с 16-битным size_t и, поскольку память продолжает дешеветь, я не вижу причин, по которым это не может произойти, даже если это не распространено .
Показать ещё 6 комментариев
3

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().

2

Имя malloc и calloc() - это функции библиотеки, которые динамически распределяют память.
Это означает, что память выделяется во время выполнения (выполнение программы) из сегмента кучи.

Инициализация: malloc() выделяет блок памяти заданного размера (в байтах) и возвращает указатель на начало блока.

>  malloc() doesn’t initialize the allocated memory. If we try to access
 the content of memory block then we’ll get garbage values. void *
> malloc( size_t size );

> calloc() allocates the memory and also initializes the allocates
 memory block to zero. If we try to access the content of these blocks
 then we’ll get 0.

> void * calloc( size_t num, size_t size );

Несколько аргументов: В отличие от malloc(), calloc() принимает два аргумента: 1) Количество блоков, которые нужно выделить. 2) Размер каждого блока.

Самое важное:

Было бы лучше использовать malloc над calloc, если мы не хотим инициализации нуля, потому что malloc быстрее, чем calloc. Поэтому, если мы просто хотите скопировать некоторые вещи или сделать то, что не требует заполнение блоков нулями, то malloc будет лучше выбор.

2

Основные отличия между malloc и calloc заключаются в следующем:

  • malloc означает выделение памяти, тогда как calloc означает непрерывное выделение.
  • malloc принимает только один аргумент, размер блока, тогда как calloc принимает два аргумента, количество блоков, которые должны быть выделены, и размер каждого блока.

    ptr = (cast-type *) malloc (размер байта)//malloc

    ptr = (cast-type *) calloc (нет блоков, размер блока);//calloc

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

2

malloc(): выделяет запрошенный размер байтов и возвращает указатель на первый байт выделенного пространства

calloc(): выделяет пространство для элементов массива, инициализирует ноль и затем возвращает указатель на память

0

malloc() принимает один аргумент, а calloc() - двоичный.

Во-вторых, malloc() не инициализирует выделенную память, а calloc() инициализирует выделенную память для ZERO. Оба malloc и calloc используются на языке C для динамического распределения памяти, они динамически получают блоки памяти.

0
char *ptr = (char *) malloc (n * sizeof(char));

просто выделяет n bytes памяти без какой-либо инициализации (т.е. эти байты памяти будут содержать любые значения мусора).

char *ptr = (char *) malloc (n, sizeof(char));

Однако метод calloc() в c инициализирует значение 0 для всех байтов занятой памяти в дополнение к функции, которую выполняет malloc().

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню