Строго говоря, все вышеперечисленные формы инициализации запрещены в стандарте C.
Объекты со статическим хранилищем допускается инициализировать с помощью следующих видов выражений (более подробно описанных здесь (со стандартными ссылками):
- выражение арифметической константы ;
- NULL константа указателя;
- выражение константы адреса ;
- выражение константы адреса некоторого полного типа объекта, плюс или минус целочисленное константное выражение.
В частности, арифметическое константное выражение может состоять из арифметических операторов, оператора sizeof и литеральных операндоварифметических типов.buf0
является переменной, а не литералом, и поэтому ни одно из выражений в примере не квалифицируется как выражение арифметической константы.Типы выражений 2, 3 и 4 также не применимы, поэтому компилятор может отклонить все формы инициализации, использующие buf0
.
Это имеет смысл, поскольку адрес buf0
разрешается только по ссылке.время, а не во время компиляции, поэтому его нельзя использовать для составления постоянных выражений времени компиляции.
Однако gcc (и другие компиляторы C, включая clang и icc) разрешат две последние формы, когда целевые объектыширина адреса и адрес назначения int width одинаковы, что является расширением.Например, на x86-64 мы получили бы:
uint64_t fails = ((uint32_t)buf0) + 2; // fails
uint64_t works_1 = ((uint64_t)buf0) + 2; // works
uint64_t works_2 = (uint64_t)buf0 + 16ul; // works
И, если мы проверим сгенерированную сборку ( godbolt ), мы увидим расширенный код gcc для works_1
и works_2
:
works_1:
.quad buf0+2
works_2:
.quad buf0+16
Ассемблер GNU допускает простую арифметику в вычислениях статических адресов с использованием +
и -
, но не позволяет использовать более сложные выражения.Теоретически, если ассемблер (и компоновщик) допускают более продвинутую адресную арифметику, такую как сдвиг, компилятор C также может допускать это расширение (но не строго соответствует).