Почему 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
Не тот строковый литерал, который был задуман, но тем не менее совершенно законный.
В отличие от этого, орграфы относительно мягки, потому что за пределами некоторых возможных странных угловых случаев, связанных с макросами, ни один код, содержащий обрабатываемые орграфы, не имел бы законного значения в отсутствие такой обработки.