Почему int _ $ [:> = <% – !. 0,}; компилировать?

Сегодня я нашел странный синтаксис, как

int _$[:>=<%-!.0,}; 

в некотором старом коде, но на самом деле код не комментируется. Кажется, нет отчета об ошибках компиляции для этой строки. Я тестировал его отдельно, и он тоже может скомпилировать:

 int main(){ int _$[:>=<%-!.0,}; return 0; } 

Почему он может скомпилировать?

С помощью Digraph (см. Ниже) линия преобразуется в:

 int _$[]={-!.0,}; 

С правой стороны, .0double литерал ! является оператором логического отрицания, - является оператором арифметического отрицания и является конечной запятой. Вместе {-!.0,} является инициализатором массива.

Левая часть int _$[] определяет массив int . Однако есть одна последняя проблема: _$ не является допустимым идентификатором в стандартном C. Некоторые компиляторы (например, gcc ) поддерживают его как расширение.


C11 §6.4.6 Пункторы

Во всех аспектах языка шесть жетонов

 <: :> <% %> %: %:%: 

ведут себя соответственно так же, как шесть токенов

 [ ] { } # ## 

Что ж,

  • underscore _ – символ разрешенного идентификатора,
  • знак доллара $ разрешен и в некоторых реализациях ,
  • левая скобка [ обозначает тип должен быть массив,
  • :> является орграфом для ] ,
  • equals = задание,
  • <% - орграф для { ,
  • -!.0 равно -1 ( .0 - двойной литеральный 0.0 ! Неявно отбрасывает (int) 0 и логически инвертирует его, а - отрицательно),
  • вы можете иметь запятые в инициализаторах массива {1, (2, 3,)},
  • и ; завершает утверждение.,

Итак, вы получаете

 int _$[] = {-1,}; 

Если мы заменим орграфы :> и <% присутствующие в вашей строке кода, мы получим

 int _$[]={-!.0,}; 

что эквивалентно

 int _$[] = { -1, }; 

Это объявление массива _$ типа int [1] с инициализатором.

Обратите внимание, что это точно не гарантируется для компиляции, поскольку стандартный язык C не обеспечивает немедленную поддержку $ character в отстудификаторах. Тем не менее, он позволяет реализациям расширять набор поддерживаемых символов. По-видимому, используемый вами компилятор поддерживал $ в идентификаторах.

Это работает благодаря орграфам в C. Линия, о которой идет речь, декодирует:

 int _$ [ :> = <% - ! .0 , } ; int _$ [ ] = { - ! 0.0 , } ; 

Более того:

  • .0 - double литерал.
  • ! является булевым оператором отрицания, поэтому !.0 дает (int) 1 .
  • - это унарный оператор отрицания, который дает (int) -1 .
  • Задняя запятая является законной после элемента массива.