Оказывается, вопрос о том, корректен или нет этот код &((T*)(0)-> x), очень сложен.
Я решил написать об этом небольшую заметку.
В последнее время статья о проверке ядра Linux С помощью анализатора PVS-Studio я написал, что нашел следующий фрагмент кода:
Я также написал в статье, что такой код, на мой взгляд, некорректен.static int podhd_try_init(struct usb_interface *interface, struct usb_line6_podhd *podhd) { int err; struct usb_line6 *line6 = &podhd->line6; if ((interface == NULL) || (podhd == NULL)) return -ENODEV; .
}
Подробности можно найти в статье.
После этого я получил электронные письма, в которых говорилось, что я был неправ и что этот код полностью правильный.
Многие отмечают, что если podhd == 0, то этот код по сути реализует идиому «offsetof», и ничего плохого произойти не может. Чтобы не писать много ответов, я решил оформить ответ в виде небольшого поста в блоге.
Естественно, я решил изучить эту тему более подробно.
Но, честно говоря, результат меня только еще больше смутил.
Поэтому я не дам вам точного ответа, можете ли вы так писать или нет. Я просто приведу несколько ссылок и поделюсь своим мнением.
Когда я писал статью о проверке Linux, я думал так.
Любое разыменование нулевого указателя является неопределенным поведением.
Одним из проявлений неопределенного поведения может быть оптимизация кода, при которой исчезает проверка (podhd == NULL).
Это именно тот сценарий, который я описал в статье.
В письмах некоторые разработчики писали, что им не удалось добиться такого поведения на своих компиляторах.
Однако это ничего не доказывает. Ожидаемая правильная работа программы — это просто один из вариантов неопределенного поведения.
Некоторые также пишут, что именно так работает макрос ffsetof(): #define offsetof(st, m) ((size_t)(&((st *)0)->m))
Однако это ничего не доказывает. Такие макросы специально разработаны для корректной работы в нужном компиляторе.
Если мы напишем подобный код, он не обязательно будет работать.
Более того, здесь компилятор явно видит 0 и может догадаться, чего от него хочет программист. Когда в переменной хранится 0, это совсем другое дело, и компилятор может вести себя неожиданным образом.
Вот о чем смещение Википедия говорит: «Традиционная» реализация макроса основывалась на том, что компилятор не особенно требователен к указателям; он получил смещение элемента, указав гипотетическую структуру, которая начинается с нулевого адреса: #define offsetof(st, m) ((size_t)(&((st *)0)-> m)) Это работает путем приведения нулевого указателя к указателю на структуру st и последующего получения адреса члена m внутри указанной структуры.
Хотя это работает правильно во многих компиляторах, оно имеет неопределенное поведение согласно стандарту C, поскольку оно включает в себя разыменование нулевого указателя (хотя можно возразить, что никакого разыменования не происходит, поскольку все выражение вычисляется во время компиляции).
Это также имеет тенденцию выдавать запутанную диагностику компилятора, если один из аргументов написан с ошибкой.
Некоторые современные компиляторы (например, GCC) вместо этого определяют макрос, используя специальную форму, например #define offsetof(st, m) __builtin_offsetof(st, m) Как видите, согласно Википедии, я прав.
Нельзя так писать.
Это неопределенное поведение.
Некоторые люди на StackOverflow думают то же самое: Адрес членов структуры через NULL-указатель .
Однако меня смущает то, что хотя все говорят о неопределенном поведении, нигде нет точного объяснения по этому поводу.
Например, в Википедии есть пометка, что утверждение требует подтверждения.
Подобные вопросы неоднократно обсуждались на форумах, но нигде я не увидел внятного объяснения, подкрепленного ссылками на стандарт C или C++.
Еще есть такое старое обсуждение стандарта, которое тоже ясности не добавило: 232. Является ли косвенность через нулевой указатель неопределённым поведением? Итак, на данный момент этот вопрос мне не совсем ясен.
Однако я все еще считаю, что этот код плох и его следует реорганизовать.
Если кто-нибудь пришлет мне хорошие заметки по этой теме, я добавлю их в конец статьи.
ОБНОВЛЕНИЕ: Продолжение: habrahabr.ru/company/pvs-studio/blog/250701 Теги: #si
-
Знак
19 Oct, 24 -
Скажите Пару Слов О Плохой Терминологии
19 Oct, 24 -
Стив Джобс Не Хочет Поддерживать Picasa
19 Oct, 24 -
Samsung Закрыла Сервис Boxee
19 Oct, 24 -
Удаленная Работа, Часть Первая, Вопросы.
19 Oct, 24 -
Хосты Оболочки В Dns
19 Oct, 24