2017-01-17 5 views
9

Я попытался понять макросы в c с помощью оператора препроцессора конкатенации ##, но я понял, что у меня проблема с токенами. Я думал, что это легко, но на практике это не так.Действительные токены препроцессора в конкатенации макросов

Таким образом, конкатенация предназначена для объединения двух токенов для создания нового токена. например: конкатенации ( и ) или int и *

Я попытался

#define foo(x,y) x ## y 
foo(x,y) 

всякий раз, когда я даю ему некоторые аргументы я получаю всегда ошибку о том, что pasting both argument does not give a valid preprocessor token.

Например, почему конкатенации foo(1,aa) результаты в 1aa (какой тип токена и почему он действительно) но foo(int,*) У меня есть ошибка.

Есть ли способ узнать, какие токены являются действительными, или возможно иметь хорошую ссылку, чтобы понять, как это можно прояснить в моем сознании. (Я уже googled в google и SO)

Что мне не хватает?

Буду благодарен.

+3

Запрашивать ссылки не по теме. И вы можете получить помощь, если вы действительно показываете, что вы передали, чтобы вызвать эту ошибку. – StoryTeller

+0

Я думаю, что '1aa' не является допустимым токеном предварительной обработки –

+0

, он не принадлежит к 6 типам токенов, но gcc ничего не говорит об этом даже не о предупреждении? – Sabrina

ответ

5

Препроцессор маркер конкатенации для генерирования новых маркеров, но он не способен оклейки произвольного языка создает вместе (придают, например, gcc documentation):

Однако две фишки, которые не делают вместе, образуют действительный токен не может быть вставлен вместе. Например, вы не можете объединить x с + в в любом порядке.

Так попытка макрос, который делает указатель из типа как

#define MAKEPTR(NAME) NAME ## * 
MAKEPTR(int) myIntPtr; 

является недействительным, так как int* две фишки, а не один.

Пример указанной выше ссылке, однако, показывает генерацию новых маркеров:

#define COMMAND(NAME) { #NAME, NAME ## _command } 

struct command commands[] = 
{ 
    COMMAND (quit), 
    COMMAND (help), 
    ... 
}; 

урожайности:

struct command commands[] = 
{ 
    { "quit", quit_command }, 
    { "help", help_command }, 
    ... 
}; 

маркеров quit_command не существовавшие раньше, но был сформирован с помощью маркера конкатенации.

Обратите внимание, что макрос вида

#define MAKEPTR(TYPE) TYPE* 
MAKEPTR(int) myIntPtr; 

действует и на самом деле генерирует тип указателя из TYPE, например, int* из int.

+0

, поэтому для объединения двух жетонов требуется создать новый существующий токен? – Sabrina

+0

токены или макро параметры ... –

+0

Я уже знаю этот пример. – Sabrina

2

Ток предварительной обработки определяется грамматикой языка С., см. Раздел 6.4 из текущего стандарта:

preprocessing-token: 
        header-name 
        identifier 
        pp-number 
        character-constant 
        string-literal 
        punctuator 
        each non-white-space character that cannot be one of the above 

Значение каждого из этих терминов определен в другом месте в грамматике. Большинство из них самоочевидны; identifier означает все, что является допустимым именем переменной (или было бы, если бы оно не было ключевым словом), а pp-number включает константы с целым числом и с плавающей запятой.

В стандарте C результат вставки двух токенов предварительной обработки должен быть еще одним допустимым токеном предварительной обработки. Исторически некоторые препроцессоры допускали другое вставку (что эквивалентно не вклеиванию!), Но это приводит к путанице, когда люди компилируют свой код с другим компилятором.

+0

, а как насчет '1aaa'? – Sabrina

+0

Использование '##' не рекомендуется. – Sabrina

+0

Я не могу понять, почему foo (-, 1) терпит неудачу, поскольку -1 будет целым числом. –

2

Так как это кажется путаницей, строка 1aa является действительным препроцессором token; это экземпляр pp-number, чье определение (§ 6.4.8 текущего стандарта C):

 pp-number: 
      digit 
      . digit 
      pp-number  digit 
      pp-number  identifier-nondigit 
      pp-number  e sign 
      pp-number  E sign 
      pp-number  p sign 
      pp-number  P sign 
      pp-number  . 

Другими словами, pp-number начинается с цифрой или ., а затем цифра, после чего она может содержать любую последовательность цифр, «идентификатор-недра» (то есть буквы, символы подчеркивания и другие элементы, которые могут быть частью идентификатора) или буквы e или p (верхний или нижний регистр), за которым следует знак плюс или минус.

Это означает, что, например, 0x1e+2 является действительным pp-number, а 0x1f+1 - нет (это три знака). В действительной программе каждый pp-number, который переживает фазы предварительной обработки, должен удовлетворять синтаксису некоторого числового представления константы, что означает, что программа, которая включает текст 0x1e+2, будет считаться недействительной. Моральная, если таковая есть, заключается в том, что вы должны щедро использовать пробелы; он не имеет стоимости.

Цель состоит в том, чтобы включить все, что могло бы быть числом в некоторой будущей версии C. (Помните, что за числами могут следовать буквенные суффиксы, указывающие типы и подпись, такие как 27LU).

Однако int* не является допустимым токеном препроцессора. Это два токена (как и -3), и поэтому он не может быть сформирован с помощью оператора конкатенации токенов.

Другим нечетным следствием правила подписи маркера является то, что невозможно создать действительный токен ... через конкатенацию маркера, поскольку .. не является допустимым токеном. (a##b##c должен быть оценен в некотором порядке, поэтому, даже если все три макроса препроцессора расширяются до ., должна быть попытка создать токен .., который не сработает в компиляторах must, хотя я считаю, что Visual Studio принимает его.)

И, наконец, символы комментариев /* и // являются не токены; комментарии заменяются пробелами перед разделением текста программы на токены. Таким образом, вы не можете создавать комментарий с использованием маркера в качестве (либо, по крайней мере, не в совместимом компиляторе).

Смежные вопросы