Страницы

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

вторник, 9 октября 2018 г.

(char *)malloc(…)

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


Ответ

Во времена динозавров в языке С не было типа 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);
которое само по себе "автоматически" подстроится под вышеупомянутое изменение типа.

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

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