Когда мне нужно передать результат 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(.)
.