Когда мне нужно передать результат malloc на языке C?

На основе этот старый вопрос malloc возвращает указатель на void, что он

автоматически и безопасно распространяется на любой другой тип указателя

Но чтение K & R Я нашел следующий код

char *strdup(char *s)
{
char *p;
/* make a duplicate of s */
p = (char *) malloc(strlen(s)+1)

В чем разница?

Ответ 1

Для любой реализации, соответствующей C89 или более поздней версии, литье результата malloc() никогда не требуется.

Вывод результата malloc() упоминается в Исправление для языка программирования C, второе издание:

142 (§6.5, к концу): Замечание о литье возвращаемого значения malloc ( "правильный метод - объявить... затем явно принудительный перевод" ), необходимо переписать. Пример правильный и работает, но совет является спорным в контексте ANSI/ISO 1988-1989 годов стандарты. Это не обязательно (учитывая, что принуждение void * to ALMOSTANYTYPE * является автоматическим) и, возможно, вредным, если malloc или прокси для него, не может быть объявлен как возвращаемый void *. Явный литье может скрыть непреднамеренную ошибку. С другой стороны, pre-ANSI, приведение было необходимо, и оно также на С++.

(Эта ссылка больше не действительна. Постепенная страница Dennis Ritchie теперь находится здесь; она указывает на обновленное местоположение для главной страницы книги, но эта ссылка также недействительна.)

Лучшей практикой является назначение результата вызова malloc() непосредственно объекту-указателю, и пусть неявное преобразование из void* заботится о согласованности типов. Объявление malloc() должно быть видимым через #include <stdlib.h>.

(Вполне возможно, что вы можете использовать malloc() в контексте, где требуется явное преобразование. Единственный случай, о котором я могу думать, - передать результат malloc() непосредственно в переменную функцию, которая нуждается в аргументе типа указателя, отличного от void*. В таком необычном и надуманном случае литье можно избежать, назначив результат объекту-указателю и передав это значение функции функции.)

Единственными вескими причинами, чтобы привести результат malloc(), являются: (a) совместимость с С++ (но необходимость писать код, который компилируется как C, так и С++ реже, чем вы могли ожидать) и (b) совместимость с pre-ANSI C (но необходимость обслуживать таких компиляторов редко и становится реже).

Ответ 2

Во-первых: K & R C является древним.

Во-вторых: я не знаю, является ли этот полный код в этом примере, но в K & R C предполагается, что функции имеют возвращаемое значение int, если не объявлено иначе. Если он не объявляет, что malloc, это означает, что он возвращает int, насколько это касается компилятора, а значит, и приведение. Обратите внимание, что это поведение undefined даже в C89 (самая ранняя стандартизованная версия) и крайне плохая практика; но он, возможно, сделал это таким образом для краткости или, возможно, из-за лени, или, может быть, для какой-то другой исторической причины; Я не знаю всех сложностей K & R C, и может быть, что отбрасывания void* to char* тогда не были имплицитными.

Я должен добавить, что это очень серьезная проблема, так как int и void* могут иметь разные размеры (и часто делают --- наиболее распространенный пример - это самый x86_64-код, где указатели - 64-разрядные, но ints 32-бит).

ОБНОВЛЕНИЕ: @KeithThompson сообщила мне, что код из второго издания, и malloc объявлен там. Этот (в отличие от 1-го) до сих пор актуальный, если он очень устарел в местах.

Я предполагаю, что приведение, скорее всего, было сделано для совместимости с несоответствующими компиляторами, что имело значение в то время, так как многие компиляторы [most?] еще не полностью соответствовали требованиям. В настоящее время вам нужно будет уйти от своего пути, чтобы найти тот, который понадобится приложению (и нет, компиляторы С++ не учитываются, это компиляторы С++, а не C).

Ответ 3

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

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

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

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

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

Сегодня я видел код:

int** x=(int**)malloc(sizeof(int)*n);

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

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