В чем преимущество обнуления памяти (т.е. calloc()
над malloc()
)? Разве вы не измените значение ни на что другое?
Calloc - Полезность обнуления памяти
Ответ 1
Есть два лагеря: один говорит, что инициализация переменных, когда они объявлены, помогает найти ошибки. Люди в этом лагере удостоверяются, что все, что они объявляют, инициализируется. Они инициализируют указатели на NULL
, int
на 0 и т.д. Идея состоит в том, что все определено, и когда они видят NULL
-поток в отладчике, они сразу же знают, что он не был установлен должным образом. Это также может помочь вашей программе сбой во время тестирования из-за NULL
-пересечения разыменования, а не таинственного сбоя в производственных прогонах.
В другом лагере говорится, что инициализация переменных при объявлении затрудняет отладку, потому что теперь компилятор не может предупредить вас о том, что переменные "используются без установки".
Не сообщая вам мои личные предпочтения 1: если вы принадлежите к первому лагерю, вам нужно calloc()
вместо malloc()
. Если вы принадлежите ко второму лагерю (что, по-видимому, вы это делаете), вы предпочитаете malloc()
над calloc()
.
Теперь есть два исключения:
- Если вы принадлежите к лагерю "инициализировать все", вы не
calloc()
, аmalloc()
, потому что вы инициализируете числа или указатели с плавающей запятой, и вы знаете, что все биты нуль не обязательно означают0
для них. Или вам не нужны дополнительные накладные расходы. - Если вы принадлежите к лагерю "установить, когда вам нужно", вы можете захотеть
calloc()
, когда вы выделяете некоторые данные и хотите, чтобы все они были нулями. Например, если вы хотите рассчитать сумму строкиn
наm
динамически распределенные данныеint
.
1 Вы можете увидеть мои ответы на многие вопросы здесь, на SO, чтобы посмотреть, в каком лагере я принадлежу: -).
Ответ 2
- Зная, какое значение уже существует, программист может принимать некоторые ярлыки и делать определенные оптимизации. Чаще всего
calloc
структура с указателями: они инициализируются значением NULL. - Что, если программист забыл инициализировать что-то в распределении? Вместо случайного материала нуль является отличным значением по умолчанию.
В системе управления процессом реального времени, в которой я работал давно, мы решили, что логика включения питания инициализирует все ОЗУ до 0xCC, инструкции 8086 interrupt 3
. Это приведет к тому, что процессор войдет в монитор (примитивный отладчик), если он каким-то образом выполнит неинициализированную память. (Unhelpfully, 8086 весело выполняет память, содержащую нули, поскольку они являются инструкциями add [bx+si],al
. Даже 32-разрядный режим вызывает их как инструкции add [ax],al
.)
Я не помню, когда мы когда-либо находили бегущую программу, но значения, соответствующие 0xCC, в разных значениях: 52 428 (без знака 16 бит), -19,660 (подписанные 16 бит), -107374176 (32-битный float), и -9.25596313493e + 61 (64-бит поплавок) появилось во множестве неожиданных мест. Кроме того, некоторый код, ожидающий символов, должен быть 7 бит ASCII, то есть ошибка, предупреждающая нас о его присутствии, когда он пытался обработать 0xCC.
Ответ 3
Предположим, что вы хотите написать реализацию сортировки подсчета, или глубину сначала найдите граф и отслеживайте посещенные вершины. Вы будете обновлять свою память по мере запуска алгоритма (вместо присвоения значения только один раз). Вам нужно инициализировать его до нуля в начале. Если у вас не было calloc
, вам придется вручную пройти через него и инициализировать его до нуля в начале вашего алгоритма. calloc
может потенциально сделать это более эффективно для вас.
Ответ 4
Хорошо знать, что все, что вы назначаете, инициализируется нулем. Многие ошибки возникли из кода, который использует неинициализированную память. Кроме того, некоторые значения по умолчанию в structs/classes могут быть точными как ноль, поэтому вам не нужно изменять все значения после malloc.
Например, выделите структуру с несколькими указателями в ней w/malloc. Проверки NULL не всегда будут работать, если они не установлены в NULL. Если вы вызываете calloc, вам не нужно выполнять дополнительные шаги инициализации для значений указателя.
Ответ 5
В дополнение к преимуществам инициализации переменных calloc также помогает отслеживать ошибки.
Если вы случайно используете бит выделенной памяти без должной инициализации, приложение всегда будет работать с ошибкой. Например, с нарушением доступа от нулевого указателя. С malloc память имеет случайные значения, и это может привести к сбою программы случайным образом.
Случайные сбои очень трудно отследить, и calloc помогает избежать этих ошибок.
Ответ 6
В первую очередь вы не можете указывать указатели на указатели, по крайней мере, если вы хотите следовать стандарту C.
Во-вторых, ошибки просто замаскируются, когда вы клонируете элемент со всеми нулями. Гораздо лучше иметь отладочную версию malloc, которая инициализирует память тем, что всегда будет сбой, например 0xCDCDCDCD.
Затем, когда вы видите сообщение Access, вы сразу же знаете проблему. Также полезно иметь функцию отладки, которая будет бить память с другим шаблоном, чтобы те, кто прикасается к памяти после ее освобождения, неожиданно неожиданно.
Работа во встроенной системе, вызов только для того, чтобы "быть уверенным", обычно не является опцией. Вы обычно выделяете и заселяете за один проход так, чтобы calloc просто mens вы дважды касались памяти.
Ответ 7
Никто не затронул аспект производительности, поэтому, я думаю, мне придется. Если вам нужно написать очень быструю программу malloc с "на всякий случай", встроенный memset - не лучший способ. Неважно, насколько быстро будет memset, он будет всегда слишком медленным. Иногда вы должны инициализировать вектор или массив, чтобы реальная проблема контролировала ваши тактовые циклы (я не трачу их впустую). Я однажды услышал цитату: "Вы никогда не должны отказываться от производительности случайно", а это означает, что с точки зрения производительности вы всегда должны знать, почему вы так или иначе решили реализовать код (что такое плюсы и минусы и как они взвешиваются против друг друга в конкретном случае).
Если у вас есть буфер, который будет заполнен строкой, это может быть "приятно иметь", чтобы инициализировать его до того, как строка будет заполнена, но большинство из них согласятся на полную потерю тактовых циклов. Если вы пишете новую функцию str *, которую вы, возможно, захотите - для целей отладки - заполните буфер значением, которое обычно не должно появляться, но это будет удалено, придет время распространения.
Как уже упоминалось, компилятор будет предупреждать, если доступ к неинициализированной переменной происходит, так как я вижу, что нижняя строка заключается в том, что на самом деле нет оправдания для инициализации "на всякий случай".