Почему GCC выдает предупреждение при использовании триграфов, а не при использовании орграфов?

Код:

#include <stdio.h>

int main(void)
{
  ??< puts("Hello Folks!"); ??>
}

Вышеуказанная программа, когда скомпилирована с GCC 4.8.1 с -Wall а также -std=c11, выдает следующее предупреждение:

source_file.c: In function ‘main’:
source_file.c:8:5: warning: trigraph ??< converted to { [-Wtrigraphs]
     ??< puts("Hello Folks!"); ??>
 ^
source_file.c:8:30: warning: trigraph ??> converted to } [-Wtrigraphs]

Но когда я меняю тело main чтобы:

<% puts("Hello Folks!"); %>

предупреждения не выбрасываются.

Итак, почему компилятор предупреждает меня при использовании триграфов, но не при использовании орграфов?

4 ответа

Решение

Этот документ gcc по предварительной обработке дает довольно хорошее обоснование для предупреждения (выделено мое):

Триграфы не популярны, и многие компиляторы реализуют их неправильно. Переносимый код не должен полагаться на преобразование или игнорирование триграфов. С -Wtrigraphs GCC предупредит вас, когда триграф может изменить смысл вашей программы, если она была преобразована.

и в этом документе gcc по токенизации объясняется, что орграфы в отличие от триграфов не имеют потенциальных негативных побочных эффектов (выделено мое):

Есть также шесть орграфов, которые стандарт C++ называет альтернативными токенами, которые являются просто альтернативными способами написания других знаков препинания. Это вторая попытка обойти пропущенную пунктуацию в устаревших системах. У него нет отрицательных побочных эффектов, в отличие от триграфов,

Потому что триграфы имеют нежелательный эффект незаметного изменения кода. Это означает, что один и тот же исходный файл действителен как с заменой триграфа, так и без нее, но приводит к другому коду. Это особенно проблематично в строковых литералах, таких как "<em>What??</em>",

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

Сравните это с орграфами, которые были новыми токенами, которые не приводят к тихим изменениям.

Может быть потому, что он не имеет отрицательных побочных эффектов, в отличие от триграфов, как указано в документации gcc:

Знаки препинания - это все обычные знаки препинания, которые имеют значение для C и C++. Все знаки препинания в ASCII, кроме трех, являются знаками C. Исключениями являются '@', '$' и '`'. Кроме того, все двух- и трехсимвольные операторы являются пунктуаторами. Есть также шесть орграфов, которые стандарт C++ называет альтернативными токенами, которые являются просто альтернативными способами написания других знаков препинания. Это вторая попытка обойти пропущенную пунктуацию в устаревших системах. У него нет отрицательных побочных эффектов, в отличие от триграфов, но он не охватывает столько места. Органы и соответствующие им нормальные знаки препинания:

 Digraph:        <%  %>  <:  :>  %:  %:%:
 Punctuator:      {   }   [   ]   #    ##

Триграфы противны, потому что они используют последовательности символов, которые могут по закону появляться в допустимом коде. Типичный случай, который вызывал ошибки компилятора в коде для классического Macintosh:

unsigned int signature = '????';  /* Should be value 0x3F3F3F3F */

Обработка триграфа превратила бы это в:

unsigned int signature = '??^;  /* Should be value 0x3F3F3F3F */

что, конечно, не скомпилировать. В некоторых более редких случаях такая обработка может привести к получению кода, который будет компилироваться, но с другим значением, нежели предполагалось, например

char *template = "????/1234";

который бы превратился в

char *template = "??S4"; // ??/ becomes \, and \123 becomes S

Не тот строковый литерал, который был задуман, но тем не менее совершенно законный.

В отличие от этого, орграфы относительно мягки, потому что за пределами некоторых возможных странных угловых случаев, связанных с макросами, ни один код, содержащий обрабатываемые орграфы, не имел бы законного значения в отсутствие такой обработки.

Другие вопросы по тегам