2013-08-27 4 views
19

Я просматривал code golf и получил представление о том, чтобы попробовать этот код:Можно ли определить другую директиву препроцессора?

#define D #define после добавления этой строки, все работало нормально, однако я расширил его в этом:

#define D #define 
D VALUE 

И вот я получил 5 ошибка компиляции. Если я изменю D на #define все в порядке, может кто-нибудь объяснить, почему этот код является незаконным?

ПРИМЕЧАНИЕ: Я использовал компилятор VS2008.

EDIT: После некоторых ответов, я вижу, что мне нужно, чтобы дать сборникам списка ошибок:

  1. ошибку C2121: «#»: недопустимый знак: возможно, в результате макроподстановок
  2. ошибки C2146 : синтаксическая ошибка: отсутствует ';' перед идентификатором «VALUE»
  3. ошибка C4430: спецификатор отсутствующего типа - int предполагается. Примечание: C++ не поддерживает default-int
  4. Ошибка
  5. Ошибка C2144: ошибка синтаксиса: перед 'void' должно предшествовать ';'
  6. ошибка C4430: отсутствует спецификатор типа - int. Примечание: C++ не поддерживает по умолчанию-ИНТ

Первая ошибка показывает, что D не только define, но и включает в себя #.

+0

Поскольку это зависит от реализации, укажите, какой компилятор вы используете. –

+4

"почему этот код является незаконным?" - Потому что вы не можете переопределить директивы препроцессора. –

+0

@ H2CO3 Я смог переопределить его, потому что первый раз он работал, однако после использования этого определения я получил ошибку компиляции. – ST3

ответ

10

Этот код является незаконным, поскольку в спецификации языка указано, что оно является незаконным. Согласно спецификации препроцессора C и C++, любой код, который вы создаете с использованием препроцессора, никогда не будет интерпретироваться как другая директива препроцессора. Короче говоря, вы не можете создавать директивы препроцессора с использованием препроцессора. Период.

(Кроме того, вы можете не создавать комментарии, используя препроцессор.)

11

Это похоже на то, что ваш препроцессор делает нужную вам замену, но вы, скорее всего, не получите желаемого поведения - препроцессор, как правило, просто однопроходный. Пример (с лязгом, но вы должны быть в состоянии воспроизвести с помощью соответствующих VS2008 флагов):

$ cat example.c 
#define D #define 
D VALUE 
$ cc -P -E example.c 

#define VALUE 

Это #define VALUE собирается прямо через компилятор, который не будет знать, что делать с ним - это препроцессорная директива, в конце концов. Ошибка лязгом, за ссылки, похож на твой:

$ cc -c example.c 
example.c:2:1: error: expected identifier or '(' 
D VALUE 
^ 
example.c:1:11: note: expanded from macro 'D' 
#define D #define 
     ^
1 error generated. 
+15

Независимо от того, является ли препроцессор однопроходной или нет, это не имеет значения. Препроцессору не нужно было бы выполнять более одного сканирования файла для обработки отдельных строк; он может обрабатывать каждую линию столько раз, сколько необходимо. Фактически, он делает это: Замена макроса выполняется повторно. Причина, по которой препроцессорные директивы не обрабатываются после замены макросов, потому что языковой стандарт говорит, что это не так. –

+0

Yup, согласился. Не знал об официальных правилах. –

6

Это не будет работать, так как предварительная обработка выполняется за один проход. Например, рассмотрим следующий код:

#define MYDEFINEWEIRD #define 

MYDEFINEWEIRD N 6 

int main() { 

    return 0; 
} 

После предварительной обработки, ваш код будет выглядеть так:

#define N 6 
int main() { 

    return 0; 
} 

и "#define" не является допустимым синтаксисом на C или C++. Кроме того, поскольку результирующая директива препроцессора не будет обрабатываться, она не будет разрешать последующие ссылки на макрос «N» в вашем коде.

Просто для удовольствия вы можете дважды вызвать препроцессор из командной строки, используя g ++/gcc. Рассмотрим следующий код (define.CPP):

#include <iostream> 

#define MYDEFINEWEIRD #define 
MYDEFINEWEIRD N 6 

using namespace std; 

int main() { 
    cout << N << endl; 
    return 0; 
} 

Тогда вы можете сделать:

$ g++ -E define.cpp | g++ -o define -x c++ - && ./define 

и выведет на экран:

6 
+2

«предварительная обработка выполняется за один проход» - нет, на самом деле это не так, и это ** не ** причина ошибки. –

+2

@ H2CO3, я думаю, что результат этого ответа выглядит правильно. –

+0

@EricPostpischil Ну, правда, он говорит, что это операция с одним проходом. Но это не так, не так ли? '#define FOO BAR', затем' #define BAR 0', затем 'return FOO;' расширен для 'return 0;' для меня. –

31

C 2011 (N1570) 6.10.3.4 3: «В результате полностью макро замененный последовательность токенов предварительной обработки не обрабатывается как директива предварительной обработки, даже если она похожа на одну, ... »

C++ 2010 (N309) 2) 16.3.4 [cpp.rescan] 3 имеет точно такой же текст.

+2

Это правильный ответ. Стандарт языка C определяет точный порядок операций в том, как перевод исходного кода программы. –

+1

Этот текст «даже если он похож на один» возвращается к стандарту ANSI C 1989 года (и, скорее всего, к ним относится проект). – Kaz

+0

N3092 не является стандартом ... хотя текст, без сомнения, тот же, что и в C++ 98, C++ 11 и каждый стандартный проект C++, который когда-либо был или будет когда-либо. – Potatoswatter

1

строк кода в предварительных процессоров глаза являются либо заявления препроцессора (и, следовательно, не имеют каких-либо замен сделали на них) или обычный текст заявления (И выполняются замены). У вас не может быть и того, и другого, поэтому, если вы замените «D», это будет выглядеть только для того, чтобы увидеть, есть ли какие-либо макросы для замены. Поскольку их нет, он просто оставляет «#define» в коде C++, как есть, и тогда компилятор C++ будет ошибочен, когда увидит его (так как «#define» не является допустимым кодом C++).

Так покажите мой пункт более, это неверный код для предварительной обработки:

#define D define 
#D value 

Поскольку предварительно процессор не делать какую-либо макросы замены на заявлениях до процессора, и «#d» не является признанной командой предварительного процессора. А это:

#define D #define 
D value 

Результаты в этом C++ код:

#define value 

Что недопустимо, так как препроцессор уже сделано запускаемых.

1

Глядя на грамматике в 16 [CPP] в пункте 1, а замещающего список состоит из рров-токенов, которые могут включать в себя производство # нет директивы которая описана в пункте 2 тот же пункт как

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

То есть, что-то в виде

#define NAME # define 

бывает незаконным! Также обратите внимание, что # в этом контексте делает не, переводя следующее слово в строку: только следующее: # shen # сразу же следует за именем макроопределения в макросе функции.

+0

Символ грамматики «неправа» в предложении «Не директива не начинается ...» относится к строке '# non-directive'. Таким образом, это просто отличные строки, которые выглядят как '# some-directive' из строк, которые выглядят как' # non-directive'. Это не делает '#define NAME # define' недопустимой последовательность токенов препроцессора. –