Лучше ли вводить указатель, возвращаемый функцией malloc? - PullRequest
4 голосов
/ 02 декабря 2009

Для кода C, приведенного ниже, сравните определения указателей int a и b;

#include <stdio.h>
#include <stdlib.h>

int main()
{
  int *a=malloc(sizeof(int));
  int *b=(int *)malloc(sizeof(int));
  return(0);
}

Лучше ли как-нибудь указывать тип указателя типа void, возвращаемого функцией malloc? Или это автоматическая типизация при назначении указателю int на левой стороне? При каких обстоятельствах, если таковые имеются, может ли это оказаться необходимым, а не просто обязательным?

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

Ответы [ 7 ]

16 голосов
/ 02 декабря 2009

Приведение не требуется для языка C, но для совместимости с C ++ может потребоваться приведение. Это одно из мест, где C не является подмножеством C ++.

Следующее будет компилироваться в C, но не в C ++:

int *a=malloc(sizeof(int));

Следующие компоненты будут компилироваться в C и C ++:

int *b=(int *)malloc(sizeof(int));
12 голосов
/ 02 декабря 2009

Нет, да, никогда

  • Некоторые люди используют указатель, а я нет.
  • Да, преобразование типов происходит "автоматически"
  • Это никогда не нужно в C.
  • Странным образом приведение результата ставит под угрозу переносимость C ++. Модуль, на который ссылается C ++, скорее всего будет скомпилирован как C, и в этом случае это не будет иметь значения. Но если код каким-то образом копируется и вставляется в C ++, я думаю, вы хотите, чтобы каждая такая строка была помечена, поскольку строка кода, вызывающая malloc () в C ++, является подозрительным LoC.
  • Частично это удержание от раннего K & R C, который не имел такого типа, как void, поэтому malloc() вернул char *. Теперь вам нужно было сделать бросок. Следовательно, шаблон проектирования приведения malloc() разработал и повлиял на всех людей, которые написали следующий набор кода, и люди все еще читают эти тела кода сегодня и несут шаблон вперед. Но у C89 была пустота, и поэтому пришло время оптимизировать код.
  • В зависимости от того, как именно это сделано, состав может нарушить DRY (он же DIE.) Как указывает Крис Лутц , это излишне снижает уровень абстракции. Поскольку это не совсем высокий уровень в C-программе, я бы предпочел не терять тот уровень, который у нас есть.
9 голосов
/ 02 декабря 2009

Проще говоря:

Если вы включили stdlib.h, как и должно быть, то приведение абсолютно ничего не делает и должно быть удалено. В конце концов, вы уже указали тип.

Если нет, то приведение закроет ошибку, что плохо.

Никогда не используйте это приведение в коде C.

В C ++ приведение необходимо, поскольку C ++ не допускает неявного преобразования между void * и другими типами указателей. С другой стороны, в C ++ есть вещи лучше, чем malloc(), например new или контейнерные классы.

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

Все, что будет делать актерский состав, это скрывать ошибки, которые могут укусить вас в другом месте. Это не поможет. Не делай этого.

8 голосов
/ 02 декабря 2009

В C приведение не требуется. malloc возвращает void*, а стандарт C утверждает:

Указатель на void может быть преобразован в или из указателя на любой неполный или тип объекта. Указатель на любой неполный или тип объекта может быть преобразован в указатель на void и вернуться снова; результат должен сравниться равно исходному указателю.

Далее, K & R2 говорит:

Любой указатель на объект может быть преобразован в тип void * без потерь. информации. Если результат преобразован обратно в исходный указатель тип, оригинальный указатель выздоровел. в отличие от преобразование указателя в указатель обсуждается в п. A.6.6, который как правило, требуется явное приведение, указатели могут быть назначены и от указатели типа void * и могут быть по сравнению с ними.

Можно сделать вывод о совместимости с C ++, поскольку в C ++ приведение является обязательным. Однако, ИМХО, это вряд ли актуально, так как идиоматический C ++ требует использовать new вместо malloc.

4 голосов
/ 02 декабря 2009

Преобразование возвращаемого значения malloc() является идиомой C ++ и не имеет места в C-коде. Это необходимо только в том случае, если вы хотите скомпилировать код как C ++, чего не следует делать, поскольку C и C ++ являются различными языками с различной семантикой.

Это имеет смысл, только если вы хотите создать библиотеку C, которая определяет встроенные функции в заголовочных файлах для использования из C ++. С появлением оптимизаций во время компоновки, мы надеемся, что в этом больше не будет необходимости, поскольку функции могут быть встроены, даже если они определены в разных исходных файлах.

Что касается людей, утверждающих, что явное приведение добавляет читабельности, рассмотрим приведенный пример

int *b=(int *)malloc(sizeof(int));

Как точно повторять тип «Три раза» более читабельно, чем

int *b = malloc(sizeof *b);

вне меня.

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

3 голосов
/ 02 декабря 2009

Если мы говорим на C, лучше всего использовать , а не , чтобы получить результат malloc(). Если мы говорим на C ++, лучше всего использовать new вместо malloc() 1 .

Причина, по которой я не рекомендую приводить результат, заключается в том, чтобы избежать проблем, если вы забудете #include stdlib.h или у вас нет прототипа для malloc() в области видимости. Вплоть до C99, если у вас был вызов функции без предварительного объявления, компилятор неявно набирал вызов функции, чтобы вернуть int. Без приведения это приведет к предупреждению о несовместимом назначении. При приведении это предупреждение подавляется, и могут возникнуть ошибки времени выполнения, поскольку значение, возвращаемое из malloc(), будет преобразовано из указателя в тип int и обратно в указатель, что, как гарантируют, не будет значимым.


  1. Если вы портируете с C на C ++, вы также можете конвертировать из malloc() в new сразу.
1 голос
/ 02 декабря 2009

В любом случае лучше ли указывать тип указателя типа void, возвращаемого функцией malloc?

Да. Это яснее по смыслу. Это та же самая причина, по которой я бы написал

a = b + (c * d);

Теперь я знаю, что "()" здесь не нужны из-за правил приоритета арифметических операторов, но они помогают мне (и другим) ясно видеть мое намерение.

$ 02 и т. Д.:)

-k

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