2016-12-21 15 views
9

Я только что узнал о функции C calloc() на днях. Прочитав его описание и как он отличается от malloc (1, 2), я понимаю, что в качестве незаполненного программиста я должен всегда использовать calloc(). Но действительно ли это так?Есть ли какая-нибудь причина НЕ использовать calloc вместо malloc?

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

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

+4

В C + почему вы хотите 'calloc()'? –

+4

C или C++? В распределении памяти на языке C++, особенно, функции * alloc недоверчивы. – NathanOliver

+0

Общее правило заключается в том, что если у вас есть проблема P, вы используете решение X, которое решает P, а не Y, что случается, чтобы решить P как побочный эффект. Там нет замены для критического мышления; программирование книгой рецептов редко работает. –

ответ

0

Это все, что вы хотите делать с памятью. malloc возвращает неинициализированную (и, возможно, еще не реальную) память. calloc return real, zero'ed memory. Если вам это нужно, нуль, то да, calloc - ваш лучший вариант. Если вы этого не сделаете, зачем платить за нуль с латентностью, когда вам это не нужно?

+0

Меня больше интересует ленивое выделение, которое предоставляет 'calloc()'. –

+0

@ VioletGiraffe Тогда вы определенно хотите 'malloc'. В Linux 'malloc' возвращает указатель, даже не указывающий на реальную память. Память становится реальной при первом обращении к ней. –

+0

@ Violet Giraffe calloc() пока не делает ленивого выделения на любой общей платформе. malloc() однако. – nos

2

Основное различие между malloc и calloccalloc является то, что будет ноль-инициализировать буфер и malloc оставит память неинициализированным.

Это относится к общей идиоме программирования «не платите за то, что вы не используете». Другими словами, зачем нулевое инициализировать что-то (что имеет стоимость), если вам необязательно (пока)?

В качестве побочного примечания с тех пор, как вы отметили C++: использование ручной памяти с использованием new/delete в современном C++ недооценено (за исключением редких случаев пулов памяти и т. Д.). Использование malloc/free является еще более редким и должно использоваться очень экономно.

6

Это действительно зависящее от ситуации решение. Правило большого пальца

  • Если вы первый запись в выделенную память, malloc() лучше (менее возможно накладные расходы).

    Пример: Рассмотрим следующий сценарий

    char * pointer = NULL; 
    
    //allocation 
    
    strcpy(pointer, source); 
    

    здесь, распределение может быть очень хорошо с помощью malloc().

  • Если есть возможность чтения перед записью с выделенной памятью, перейдите на calloc(), так как он инициализирует память. Таким образом, вы можете избежать проблемы со сценарием со стандартизованной памятью, записанным до записи, который вызывает undefined behavior.

    Пример:

    char * pointer = NULL; 
    
    //allocation 
    
    strcat(pointer, source); 
    

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

Разработать второй сценарий, цитаты из C11, глава §7.24.3.1 (следовать моему вниманию)

strcat() функция присоединяет копию строки, указанная s2 (включая завершающий нулевой символ) до конец строки, на которую указывает s1. Начальный символ из s2 перезаписывает нулевой символ в конце s1. [....]

Итак, в этом случае указатель адресата должен быть указателем на строку. Выделяя через calloc() гарантирует, что при выделении с помощью malloc() не может гарантировать, что, как мы знаем, из главы §7.22.3.4

malloc функция выделяет место для объекта, размер которого определяется size и , значение которого является неопределенным ,


EDIT:

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

+0

Аспект нулевой инициализации довольно ясен. Но как насчет ленивого распределения памяти, которое предоставляет 'calloc()'? –

+0

@ VioletGiraffe AFAIK, который является свойством распределителя памяти и применим в равной степени с 'malloc()' и/или 'calloc()' для конкретной платформы. Можете ли вы уточнить? –

+0

Ссылки в моем вопросе: http://stackoverflow.com/a/2688522/634821 и https://vorpus.org/blog/why-does-calloc-exist/ –

1

Используйте calloc для нулевых значений, но только тогда, когда нулевое заполнение действительно необходимо.

Вы должны всегда использовать calloc(count,size) вместо buff=malloc(total_size); memset(buff,0,total_size).

Звонок на нуль-memset является ключом. Оба malloc и calloc переведены на вызовы ОС, которые делают множество оптимизаций, используют аппаратные трюки, когда это возможно, и т. Д. Но мало того, что ОС может делать с memset.

С другой стороны, когда вам нужно заполнить выделенную память нулем? Единственное общее применение - для элементов произвольной длины с нулевым концом, таких как C-строки. Если это так, конечно, пойдите с calloc.

Но если вы выделяете структуры, в которых элементы являются либо фиксированными, либо несут длину с ними произвольных размеров (например, строки и векторы C++), то заполнение нуля не является полезным вообще, и если вы пытаетесь полагаться на него, это может привести к сложным ошибкам.

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

Для целей отладки нулевое заполнение обычно не так уж и хорошо. 0 слишком часто, вы редко можете написать что-то вроде assert(size);, потому что это обычно действительное значение, вы тоже обрабатываете его с помощью if(!size), а не с утверждениями. На отладчике это тоже не попадет вам в глаза. В вашей памяти обычно есть нули. Лучшая практика заключается в том, чтобы избежать неподписанных типов для длин (подписанные длины могут быть полезны для обработки ошибок во время выполнения и некоторых из наиболее распространенных проверок переполнения). Таким образом, в то время как buff=malloc(total_size); memset(buff,0,total_size) следует избегать, следующие в порядке:

const signed char UNINIT_MEM=MY_SENTINEL_VALUE; 
buff=malloc(total_size); 
#if DEBUG_MEMORY 
memset(buff,UNINIT_MEM,total_size); 
#endif 

В режиме отладки, библиотеки времени выполнения или даже операционной системы сделать это для вас иногда, например, проверка this excellent post on VC++-specific sentinel values.

0

malloc() гораздо чаще встречается в коде C, чем calloc().

Текстовый поиск «malloc» пропустит вызовы calloc().

Замена libraires часто будет иметь mymalloc(), myrealloc(), myfree(), но не mycalloc().

Нулевой инициализации указателей и реалов на самом деле не гарантируется ожидаемый эффект, хотя на каждой основной платформе все биты нуля равны NULL для указателя и 0.0 для реального.

calloc() стремится скрыть ошибки. Debug malloc обычно задает шаблон заполнения, такой как DEADBEEF, который оценивает большое отрицательное число и не выглядит как реальные данные. Таким образом, программа быстро сбой и, с отладчиком, ошибка вымывается.