Я использую результат malloc? - PullRequest
2250 голосов
/ 03 марта 2009

В этот вопрос , кто-то предложил в комментарии , что я должен не привести результат malloc, т. Е.

int *sieve = malloc(sizeof(int) * length);

вместо:

int *sieve = (int *) malloc(sizeof(int) * length);

Почему это так?

Ответы [ 26 ]

2073 голосов
/ 03 марта 2009

Нет ; Вы не разыгрываете результат, так как:

  • В этом нет необходимости, поскольку void * автоматически и безопасно переводится в любой другой тип указателя в этом случае.
  • Это добавляет беспорядок в код, приведения не очень легко читать (особенно если тип указателя длинный).
  • Это заставляет вас повторяться, что обычно плохо.
  • Может скрыть ошибку, если вы забыли включить <stdlib.h>. Это может вызывать сбои (или, что еще хуже, , а не - вызывать сбои в более поздней части совершенно другой части кода). Подумайте, что произойдет, если указатели и целые числа имеют разные размеры; тогда вы скрываете предупреждение путем приведения и можете потерять биты вашего возвращенного адреса. Примечание: начиная с C11 неявные функции ушли из C, и этот момент больше не актуален, поскольку нет автоматического предположения, что необъявленные функции возвращают int.

В качестве пояснения обратите внимание, что я сказал: «Вы не разыгрываете», а не «вам не нужно , чтобы разыграть ». На мой взгляд, это не удалось включить актерский состав, даже если вы правильно поняли. В этом нет просто никакой выгоды, но куча потенциальных рисков, и в том числе актерский состав, указывает на то, что вы не знаете о рисках.

Также отметьте, как отмечают комментаторы, что выше говорится о прямом C, а не C ++. Я очень твердо верю в C и C ++ как отдельные языки.

Чтобы добавить, ваш код без необходимости повторяет информацию о типе (int), что может вызвать ошибки. Лучше разыменовать указатель, используемый для хранения возвращаемого значения, чтобы «заблокировать» их вместе:

int *sieve = malloc(length * sizeof *sieve);

Это также перемещает length вперед для повышения видимости и удаляет лишние скобки с sizeof; они необходимы только , когда аргумент является именем типа. Многие люди, кажется, не знают (или игнорируют) это, что делает их код более многословным. Помните: sizeof это не функция! :)


При перемещении length вперед может увеличить видимость в некоторых редких случаях, следует также обратить внимание, что в общем случае лучше написать выражение как:

int *sieve = malloc(sizeof *sieve * length);

Так как сохранение sizeof первым, в этом случае обеспечивает умножение по крайней мере с size_t математикой.

Сравните: malloc(sizeof *sieve * length * width) против malloc(length * width * sizeof *sieve) секунда может переполнить length * width, когда width и length меньше, чем size_t.

351 голосов
/ 03 марта 2009

В C вам не нужно приводить возвращаемое значение malloc. Указатель на void, возвращаемый malloc, автоматически преобразуется в правильный тип. Однако, если вы хотите, чтобы ваш код компилировался с помощью компилятора C ++, необходимо преобразование. Предпочтительной альтернативой среди сообщества является использование следующего:

int *sieve = malloc(sizeof *sieve * length);

, что дополнительно освобождает вас от необходимости беспокоиться об изменении правой части выражения, если вы когда-либо измените тип sieve.

Кастинги плохие, как указывали люди. Специально указатель отливок.

326 голосов
/ 14 февраля 2013

Вы делаете разыгрывание, потому что:

  • Это делает ваш код более переносимым между C и C ++, и, как показывает опыт SO, многие программисты утверждают, что пишут на C, когда они действительно пишут на C ++ (или C плюс локальные расширения компилятора). ).
  • Если этого не сделать может скрыть ошибку : обратите внимание на все SO примеры путаницы, когда писать type * против type **.
  • Идея, что он не замечает, что вам не удалось #include соответствующий заголовочный файл, пропускает лес для деревьев . Это то же самое, что сказать: «Не беспокойтесь о том, что вы не попросили компилятор жаловаться на то, что не видите прототипы - этот pesky stdlib.h - ДЕЙСТВИТЕЛЬНО важная вещь, которую нужно запомнить!»
  • Вызывает дополнительную когнитивную перекрестную проверку . Он помещает (предполагаемый) желаемый тип прямо рядом с арифметикой, которую вы делаете для необработанного размера этой переменной. Могу поспорить, что вы могли бы сделать SO-исследование, которое показывает, что malloc() ошибки обнаруживаются намного быстрее, когда происходит бросок. Как и с утверждениями, аннотации, раскрывающие намерения, уменьшают количество ошибок.
  • Повторять себя так, как машина может проверить, - это часто замечательная идея. Фактически, это то, что является утверждением, и это использование приведения является утверждением. Утверждения по-прежнему являются наиболее общей техникой, которую мы используем для получения правильного кода, поскольку Тьюринг выдвинул эту идею много лет назад.
160 голосов
/ 03 марта 2009

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

#ifdef __cplusplus
# define NEW(type, count) ((type *)calloc(count, sizeof(type)))
#else
# define NEW(type, count) (calloc(count, sizeof(type)))
#endif

Таким образом, вы все еще можете написать его очень компактно:

int *sieve = NEW(int, 1);

и он скомпилируется для C и C ++.

119 голосов
/ 10 октября 2015

Из Википедии :

Преимущества для литья

  • Включение преобразования может позволить программе или функции на C компилироваться как C ++.

  • Приведение допускает версии malloc, выпущенные до 1989 года, которые изначально возвращали символ *.

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

Недостатки в кастинге

  • В соответствии со стандартом ANSI C приведение является избыточным.

  • Добавление приведения может маскировать невозможность включения заголовка stdlib.h в который найден прототип для malloc. В отсутствие прототип для malloc, стандарт требует, чтобы компилятор C предположим, что malloc возвращает int. Если нет приведения, предупреждение выдается, когда это целое число назначено указателю; однако с В касте это предупреждение не выдается, скрывая ошибку. По определенным архитектуры и модели данных (такие как LP64 в 64-битных системах, где long и указатели 64-битные, а int 32-битные), эта ошибка может на самом деле приводит к неопределенному поведению, как неявно объявлено malloc возвращает 32-битное значение, тогда как фактически определенная функция возвращает 64-битное значение. В зависимости от соглашения о вызовах и памяти макет, это может привести к разрушению стека. Эта проблема менее вероятна остаться незамеченным в современных компиляторах, так как они производят единообразно предупреждения о том, что была использована необъявленная функция, поэтому предупреждение все еще появляются. Например, стандартным поведением GCC является показ предупреждение, которое гласит «несовместимое неявное объявление встроенного функция "независимо от того, присутствует ли приведение или нет.

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

Хотя malloc без приведения является предпочтительным методом, и большинство опытных программистов выбирают его , вы должны использовать все, что захотите, зная о проблемах.

Т.е.: если вам нужно скомпилировать программу на C как C ++ (хотя это отдельный язык), вы должны использовать malloc с приведением типов.

98 голосов
/ 03 марта 2009

В C вы можете неявно преобразовывать указатель void в любой другой тип указателя, поэтому приведение не требуется. Использование одного может подсказать случайному наблюдателю, что есть какая-то причина, по которой он нужен, что может ввести в заблуждение.

90 голосов
/ 20 марта 2014

Вы не приводите результат malloc, потому что это добавляет бесполезный беспорядок в ваш код.

Самая распространенная причина, по которой люди приводят результат malloc, заключается в том, что они не уверены в том, как работает язык Си. Это предупреждающий знак: если вы не знаете, как работает тот или иной языковой механизм, не сделайте предположение. Найдите это или спросите о переполнении стека.

Некоторые комментарии:

  • Пустой указатель можно преобразовать в / из любого другого типа указателя без явного приведения (C11 6.3.2.3 и 6.5.16.1).

  • C ++, однако, не допускает неявное приведение между void* и другим типом указателя. Так что в C ++ приведение было бы правильным. Но если вы программируете на C ++, вы должны использовать new, а не malloc (). И вы никогда не должны компилировать код C, используя компилятор C ++.

    Если вам нужно поддерживать C и C ++ с одним и тем же исходным кодом, используйте переключатели компилятора, чтобы отметить различия. Не пытайтесь насытить оба языковых стандарта одним и тем же кодом, потому что они несовместимы.

  • Если компилятор C не может найти функцию, потому что вы забыли включить заголовок, вы получите ошибку компилятора / компоновщика об этом. Поэтому, если вы забыли включить <stdlib.h>, это не важно, вы не сможете собрать свою программу.

  • На древних компиляторах, которые следуют версии стандарта, которой более 25 лет, забыв включить <stdlib.h>, это может привести к опасному поведению. Потому что в этом древнем стандарте функции без видимого прототипа неявно преобразовывали возвращаемый тип в int. Явное приведение результата из malloc могло бы скрыть эту ошибку.

    Но это действительно не проблема. Вы не используете компьютер 25 лет, так зачем вам использовать компилятор 25 лет?

85 голосов
/ 03 марта 2009

В C вы получаете неявное преобразование из void* в любой другой указатель (данных).

66 голосов
/ 09 июня 2013

Приведение значения, возвращаемого malloc(), теперь не нужно, но я хотел бы добавить одну точку, которая, кажется, никто не указал:

В древние времена, то есть до того, как ANSI C предоставит void * в качестве универсального типа указателей, char * является типом для такого использования. В этом случае приведение может отключить предупреждения компилятора.

Ссылка: C FAQ

49 голосов
/ 08 февраля 2013

Не обязательно приводить результаты malloc, так как он возвращает void*, и void* может указывать на любой тип данных.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...