Страницы

Поиск по вопросам

четверг, 5 декабря 2019 г.

(char *)malloc(…)

#c #преобразование_типов


В некоторых отрывках кода наблюдаю приведение malloc() к (char *), (int *) и т.п.
Зачем это делается?

Например:

char *p;
p = (char *)malloc(100*sizeof(char));

    


Ответы

Ответ 1



Во времена динозавров в языке С не было типа void *. Идея универсального указателя void * пришла в С намного позже (причем не откуда-нибудь, а из С++). А до того времени в качестве "универсального" указательного типа использовался именно char *. Функция malloc возвращала результат именно типа char *. Именно в те времена и существовала необходимость для выполнения неявного приведения результата malloc от char * к конкретному типу указателя-получателя. В примерах кода, написанных на достандартном С, включая первые версии K&R, это приведение везде присутствует именно по этой причине. С появлением в языке типа void *, неявно приводящегося к любому указательному типу, и с последующим переходом malloc на void *, необходимость в явном приведении отпала. Однако в большом количестве старого кода его еще можно увидеть. Это приведение перекочевало и во второе издание K&R С, написанное еще до появления первого стандарта С. Несмотря на то, что второе издание задним числом привели в соответствие со стандартом С89/90, примеры кода в нем никто исправлять не стал. В результате студенты, использующие книгу K&R для обучения, "нахватываются" этот странной привычки оттуда, слепо следуя ей как "карго культу". В современном С-коде выполнять такое приведение имеет смысл разве что только в кросс-компилируемом С-С++ коде, т.к. в С++ указатель void * к другим типам сам по себе не приводится. В частности, например, при реализации кросс-компилируемых макросов и inline-функций в заголовочных файлах. (Наследием тех времен, когда в качестве универсального указателя в С использовался тип char * в современной спецификации языка является гарантия того, что объектное представление и требования выравнивания типов void * и char * обязаны совпадать.) Справедливости ради надо заметить, что один аргумент современных любителей явно приводить результат malloc в С коде (не предназначенном для кросс-компиляции) таки содержит определенное рациональное зерно. В коде вроде T* p; ... p = (T *) malloc(N * sizeof(T)); если кто-то поменяет тип указателя в объявлении p с T * на U *, но при этом забудет исправить выделение памяти через malloc, то в вызове malloc компилятором будет сгенерировано диагностическое сообщение о несоответствии типов указателей. Да, это действительно так. Однако более грамотным вариантом защиты от подобных проблем будет использование идиомы с "типонезависимым" выделением памяти p = malloc(N * sizeof *p); которое само по себе "автоматически" подстроится под вышеупомянутое изменение типа.

Ответ 2



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

Ответ 3



Соглашаясь с предыдущими ораторами, я выскажу свою крамольную мысль - потому что многие ухитряются считать С чем-то вроде кастрированного С++, и компилировать программы на С в режиме С++. А он требует приведения... Так что все эти приведения - просто затычка, чтоб компилятор не ругался, перешедшая в привычку :) Да оно и не так уж плохо - по крайней мере видно, для чего именно malloc память выделял.

Комментариев нет:

Отправить комментарий