Теперь, прежде чем люди начнут отмечать это как дубли, я прочитал все следующее, ни одно из которых не дает ответа, который я ищу:
- C FAQ: Что не так с приведением возвращаемого значения malloc?
- SO: Должен ли я явно приводить возвращаемое значение malloc ()?
- SO: ненужное приведение указателей в C
- ТАК: Я использую результат malloc?
Как в FAQ по C, так и во многих ответах на приведенные выше вопросы упоминается загадочная ошибка, которую malloc
может скрыть возвращаемое значение приведения; однако ни один из них не дает конкретного примера такой ошибки на практике. Теперь обратите внимание, что я сказал об ошибке , а не о предупреждении .
Теперь, учитывая следующий код:
#include <string.h>
#include <stdio.h>
// #include <stdlib.h>
int main(int argc, char** argv) {
char * p = /*(char*)*/malloc(10);
strcpy(p, "hello");
printf("%s\n", p);
return 0;
}
Компиляция приведенного выше кода с помощью gcc 4.2, с приведением и без него, дает одинаковые предупреждения, и программа выполняется правильно и дает одинаковые результаты в обоих случаях.
anon@anon:~/$ gcc -Wextra nostdlib_malloc.c -o nostdlib_malloc
nostdlib_malloc.c: In function ‘main’:
nostdlib_malloc.c:7: warning: incompatible implicit declaration of built-in function ‘malloc’
anon@anon:~/$ ./nostdlib_malloc
hello
Так может ли кто-нибудь привести конкретный пример кода ошибки компиляции или выполнения, которая могла произойти из-за malloc
возвращаемого значения приведения, или это просто городская легенда?
Изменить Я встретил два хорошо написанных аргумента по этому поводу:
- В пользу приведения: CERT Advisory: немедленно привести результат вызова функции выделения памяти в указатель на выделенный тип
- Против трансляции (ошибка 404 от 14.02.2012: используйте копию Internet Archive Wayback Machine от 27.01.2010. {2016-03-18: "Страница не может сканироваться или отображаться из-за файла robots.txt."})
void
указателей позволяет компилировать код как C ++; некоторые люди говорят, что это особенность, я бы сказал, что это ошибка;)malloc
возвращаемого значения приведения: приведение кint*
64-битной арке.C
неC++
(они два разных языка) Таким образом , любое обсуждение (как и в некоторых из ответов) не имеет отношения к этому вопросу.Ответы:
Вы получите не ошибку компилятора , а предупреждение компилятора . Как говорят источники, которые вы цитируете (особенно первый ), вы можете получить непредсказуемую ошибку времени выполнения при использовании приведения без включения
stdlib.h
.Так что ошибка на вашей стороне не в приведении, а в забвении включения
stdlib.h
. Компиляторы могут предположить, чтоmalloc
это функция, возвращающаяint
, поэтому преобразованиеvoid*
указателя, фактически возвращенного функцией ,malloc
в,int
а затем в ваш тип указателя из-за явного приведения. На некоторых платформахint
указатели могут занимать разное количество байтов, поэтому преобразование типов может привести к повреждению данных.К счастью, современные компиляторы выдают предупреждения, указывающие на вашу фактическую ошибку. См.
gcc
Предоставленный вами вывод: он предупреждает вас, что неявное объявление (int malloc(int)
) несовместимо со встроеннымmalloc
. Так чтоgcc
вроде знаетmalloc
даже безstdlib.h
.Отказ от приведения во избежание этой ошибки в основном аналогичен написанию
if (0 == my_var)
вместо того
if (my_var == 0)
поскольку последний может привести к серьезной ошибке, если запутать
=
и==
, тогда как первый приведет к ошибке компиляции. Лично я предпочитаю второй стиль, поскольку он лучше отражает мои намерения, и я не склонен делать эту ошибку.То же самое верно и для приведения значения, возвращаемого
malloc
: Я предпочитаю быть явным в программировании и обычно дважды проверяю, чтобы включить файлы заголовков для всех функций, которые я использую.источник
stdlib.h
уже само по себе является ошибкой, даже если вы получаете только предупреждения о «неявном объявлении».Один из хороших аргументов более высокого уровня против приведения результата
malloc
часто не упоминается, хотя, на мой взгляд, он более важен, чем известные проблемы более низкого уровня (например, усечение указателя при отсутствии объявления).Хорошая практика программирования - писать код, максимально независимый от типа. Это, в частности, означает, что имена типов следует как можно меньше упоминать в коде или, лучше всего, вообще не упоминать. Это относится к приведениям типов (избегайте ненужных приведений), типам в качестве аргументов
sizeof
(избегайте использования имен типов вsizeof
) и, как правило, ко всем другим ссылкам на имена типов.Имена типов входят в объявления. Насколько это возможно, имена типов следует ограничивать объявлениями и только объявлениями.
С этой точки зрения этот фрагмент кода плохой
int *p; ... p = (int*) malloc(n * sizeof(int));
и это намного лучше
int *p; ... p = malloc(n * sizeof *p);
не просто потому, что он "не приводит к результату
malloc
", а потому, что он не зависит от типа (или не зависит от типа, если вы предпочитаете), потому что он автоматически подстраивается под любой тип,p
с которым объявлен, без какого-либо вмешательства со стороны Пользователь.источник
Предполагается, что функции, не являющиеся прототипами, возвращаются
int
.Итак, вы вводите
int
указатель. Если указателиint
на вашей платформе шире, чем s, это очень рискованное поведение.Плюс, конечно, что некоторые люди считают предупреждения быть ошибки, т.е. код должен компилироваться без них.
Лично я считаю, что тот факт, что вам не нужно
void *
приводить к другому типу указателя, является особенностью C, и считаю, что код, который действительно, сломан.источник
void*
.int
». - Вы имеете в виду, что можно изменить тип возврата непрототипированных функций?#ifdef __cplusplus \nextern "C" { \n#endif static inline uint16_t swb(uint16_t a) {return ((a << 8) | ((a >> 8) & 0xFF); } \n#ifdef __cplusplus\n } \n#endif
. Я действительно не знаю, почему вы хотите вызывать malloc в статической встроенной функции, но вряд ли можно услышать заголовки, которые работают в обоих.Если вы сделаете это при компиляции в 64-битном режиме, ваш возвращаемый указатель будет усечен до 32-битного.
РЕДАКТИРОВАТЬ: Извините, что был слишком кратким. Вот пример фрагмента кода для обсуждения.
Предположим, что возвращаемый указатель кучи является чем-то большим, чем то, что может быть представлено в int, скажем 0xAB00000000.
Если прототип malloc не возвращает указатель, возвращаемое значение int изначально будет в каком-то регистре со всеми установленными значащими битами. Теперь компилятор говорит: «Хорошо, как мне преобразовать int в указатель». Это будет либо знаковое, либо нулевое расширение младших 32-битных битов, которые, как было сказано, malloc «возвращает», опуская прототип. Поскольку int подписан, я думаю, что преобразование будет расширением знака, которое в этом случае преобразует значение в ноль. С возвращаемым значением 0xABF0000000 вы получите ненулевой указатель, который также вызовет некоторые забавы при попытке разыменовать его.
источник
Правило многоразового программного обеспечения:
В случае написания встроенной функции, в которой используется malloc (), чтобы сделать ее многоразовой и для кода C ++, выполните явное приведение типа (например, (char *)); иначе компилятор пожалуется.
источник
malloc
/free
для обоих склонны быть лучше , чем пытаться использоватьmalloc
в C иnew
в C ++, особенно если структуры данных разделены между C и C ++ код, и есть вероятность того, что объект может быть создан в коде C и выпущен в коде C ++ или наоборот.Указатель void в C может быть назначен любому указателю без явного приведения. Компилятор выдаст предупреждение, но его можно повторно использовать в C ++ путем приведения типа
malloc()
к соответствующему типу. Без приведения типов его также можно использовать в C , потому что C не является строгой проверкой типов . Но C ++ строго проверяет типы, поэтому необходимо вводить приведениеmalloc()
в C ++.источник