Ответ 1
Существует множество программ, которые считались бы действительными в соответствии с C89 до публикации C99, которые некоторые люди настаивают, что они никогда не были действительными. C89 содержит правило, которое требует, чтобы к объекту любого типа можно было получить доступ только с помощью указателя этого типа, связанного типа или типа символа. До публикации C99 это правило обычно интерпретировалось как применяемое только к "именованным" объектам (переменные статической или автоматической продолжительности, к которым обращаются напрямую по имени), и только в ситуациях, когда объект, о котором идет речь, не имел своего адреса взятых непосредственно перед тем, как он использовался как другой тип указателя. Такая интерпретация была обусловлена рядом факторов:
-
Одна из заявленных целей Стандарта заключалась в том, чтобы соответствовать тем, что делали существующие компиляторы и программы, и хотя для существующих программ было бы редко обращаться к дискретным именованным переменным, используя указатели разных типов, кроме случаев где переменный адрес был взят непосредственно перед таким использованием, многие другие обычаи пейнинга типа указателя были довольно распространены.
-
Обоснование Стандарта включает в качестве единственного примера функцию, которая получает указатель одного примитивного типа для записи глобальной переменной другого примитивного типа таким образом, что у компилятора не будет особых причин ожидать сглаживания, Возможность сохранять глобальные переменные в регистрах явно полезная оптимизация, и заявленная цель этого правила заключается в том, чтобы разрешить такую оптимизацию в тех случаях, когда у компилятора не было причин ожидать появления псевдонимов. Конструкции вне закона, такие как
(int*)&foo=23;
, ничего не помогают таким оптимизациям, поскольку тот факт, что код принимает адресfoo
и разыменовывает его, должен сделать его совершенно ясным для любого компилятора, который не намеренно тупит, что код собирается изменитьfoo
. -
Существует много видов кода, которые семантически требуют использования битов памяти как разных типов, и ничто в Стандарте не указывает, что правила предназначены для того, чтобы программисты переходили через обручи (например, используя memcpy) для достижения семантику, которую можно было бы легко получить при отсутствии правил, особенно учитывая, что использование memcpy помешает компилятору сохранить глобальные переменные в регистрах через обращения к указателям (таким образом, побеждая цель, для которой правила были написаны в первую очередь).
-
Если типы структуры
V
иW
имеют общую начальную последовательность,U
- любой тип объединения, содержащий оба, аp
- этоV*
, который идентифицируетV
внутри aU
, то(W*)(U*)p
может использоваться для доступа к этим общим членам и будет эквивалентен(W*)p
. Если компилятор не мог показать, чтоp
не может быть указателем на член какого-либо объединения, содержащегоW
, требуется, чтобы(W*)p
имел доступ к общим членам; было бы более полезно просто рассматривать такой общий доступ к члену как законный, независимо от того, может ли существоватьU
, чем искать оправдания, чтобы отрицать его. -
Ничто в правилах C89 не дает четкого представления о том, как определяется "тип" области выделенного хранилища или как хранилище, в котором хранятся вещи одного типа, которые больше не нужны, могут быть переназначены для хранения вещей другой.
-
Отслеживание регистров, назначенных именованным переменным, было проще, чем отслеживать регистры, выделенные для других исключений указателей, а код, который был заинтересован в минимизации количества загрузок и хранилищ с помощью указателей, часто копировал вещи в именованные переменные и работайте над ними.
В C99 добавлены правила "эффективного типа", которые явно применимы к выделенному хранилищу. Некоторые настаивают на том, что это просто "разъяснения" правил, которые уже существовали на C89, но по вышеуказанным причинам я считаю, что точка зрения несостоятельна. Модно утверждать, что единственными причинами, по которым компиляторы не применяли правила псевдонимов для неназванных объектов, являются # 5 и # 6, но возражения # 1- # 4 одинаково значимы (и продолжают применяться к C99 так же сильно, как C89). Тем не менее, поскольку C99 добавил эффективные правила типа, многие конструкции, которые считались бы законными большинством распространенных интерпретаций правил C89, явно запрещены.