Передача значения переменной в макрос в C

Я пытаюсь передать значение переменной в макрос на C, но не знаю, возможно ли это. Пример:

#include <stdio.h>

#define CONCVAR(_n) x ## _n

int main () {
   int x0, x1, x2, x3, x4, x5, x6, x7, x8, x9;
   int i;

   for (i = 0; i <= 9; i++) CONCVAR(i) = i*5;

   return 0;
}

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

CONCVAR(i) заменяется на xi, а не на x1 (если i == 1). Я знаю, как работают определения и макросы, все дело в подстановке, но я хочу знать, можно ли передать значение i вместо буквы i в макрос.


person Fábio Perez    schedule 16.02.2011    source источник
comment
Вы должны просто использовать массивы. Он никогда не будет работать таким образом, и даже если бы он работал, из него мало что можно было бы извлечь, кроме того, что это просто ужасный код. Если вы хотите обойти массивы, используйте вместо этого указатели (и будьте готовы к множеству дыр в ногах).   -  person    schedule 16.02.2011
comment
@delnan: Это больше похоже на упражнение в использовании/понимании ##, чем в использовании массивов, но я не уверен. en.wikipedia.org/wiki/C_preprocessor#Token_concatenation   -  person FrustratedWithFormsDesigner    schedule 16.02.2011
comment
Как правило, каждый макрос, напоминающий функцию или принимающий параметры, является плохой практикой программирования.   -  person Lundin    schedule 16.02.2011
comment
@delnan Я знаю, что лучше использовать массивы, но я просто пытаюсь учиться на этом примере. ;)   -  person Fábio Perez    schedule 16.02.2011
comment
@Frustrated: я знаю, что это в образовательных целях (это в вопросе), и ОП узнает кое-что об этом о препроцессоре (а именно, когда он будет расширен). Но все равно это ужасная идея.   -  person    schedule 16.02.2011
comment
Мне любопытно: вы действительно пробовали скомпилировать/запустить этот код, чтобы посмотреть, что произойдет?   -  person FrustratedWithFormsDesigner    schedule 16.02.2011
comment
Что касается обучения, вам не нужно тренироваться с использованием ##, это лишняя функция. Вам это нужно примерно так же, как вам нужны триграфы или любая другая такая непонятная, лишняя синтаксическая функция.   -  person Lundin    schedule 16.02.2011
comment
@Frustrated Да, CONCVAR(i) преобразуется в xi, и такой переменной нет, поэтому она не компилируется.   -  person Fábio Perez    schedule 16.02.2011
comment
@Lundin: Ну, это может использоваться. Довольно редко, да, и в этих случаях вы можете рассмотреть возможность использования языка более высокого уровня, который в любом случае имеет функцию, которую вы эмулируете, но в отличие от триграфов, которые, вероятно, используются в некоторых реальных проектах.   -  person    schedule 16.02.2011
comment
@Lundin Я знаю, что это не важная функция, но мне было любопытно.   -  person Fábio Perez    schedule 16.02.2011
comment
@delnan Судя по слухам, триграфы были добавлены в стандарт, потому что в Дании были странные клавиатуры, в которых отсутствовали некоторые необходимые символы, используемые в C. Так что да, все в языке используется где-то в реальный мир в порядке. Но это не означает, что каждый программист на C должен глубоко изучать триграфы или использовать их только потому, что они существуют.   -  person Lundin    schedule 16.02.2011
comment
@Lundin: Триграфы были введены, потому что в некоторых частях мира тогда даже не было кодировок и клавиатур, совместимых с ASCII. Но это давно закончилось. Я сомневаюсь, что любой код, написанный за последние 20 лет, использует триграфы, за исключением, возможно, чрезвычайно редких (например, 1 проектов на миллион) и странных вещей. Поэтому я не думаю, что кто-то должен изучать триграфы. Однако используются # и ## (см., например, код Google).   -  person    schedule 16.02.2011
comment
@Lundin, препроцессор C - чрезвычайно полезный инструмент, поскольку C и C ++ являются ограниченными и ограничивающими языками. Для вдохновляющей демонстрации использования ## и макросов в целом взгляните на исходники LLVM и Clang, особенно на то, как используются файлы .def и .inc. Нет лучшего способа сделать то же самое с таким ограниченным языком, как C++.   -  person SK-logic    schedule 16.02.2011


Ответы (3)


Подстановка значения i в макрос невозможна, поскольку подстановка макросов происходит до компиляции вашего кода. Если вы используете GCC, вы можете увидеть вывод препроцессора, добавив аргумент командной строки '-E' (однако обратите внимание, что вы увидите все #include, вставленные в ваш код).

C — статический язык, и вы не можете выбирать имена символов во время выполнения. Однако то, чего вы пытаетесь достичь, возможно, если вы используете массив и ссылаетесь на элементы, используя индексы. Как правило, если у вас много переменных, таких как x0, x1 и т. д., вам, вероятно, следует использовать контейнер, например массив.

person yan    schedule 16.02.2011

Нет, так как значение i существует только во время времени выполнения. Расширение макроса происходит во время компиляции.

person Oliver Charlesworth    schedule 16.02.2011

Нет, это не сработает. Препроцессор C/C++ — это всего лишь текстовый процессор времени «прекомпиляции». Таким образом, он работает с текстом, найденным как есть в вашем исходном коде.

Вот почему он берет буквальный текст «i», передает его в ваш макрос, расширяя его в буквальный текст «xi» в вашем исходном коде. Затем это передается компилятору. Затем компилятор начинает синтаксический анализ постобработанного текста, находя литеральный токен «xi» как необъявленную переменную, и в процессе идет на дно.

Вы можете взять свой пример исходного кода и передать его компилятору gcc (например, я использовал gcc под cygwin, вставив ваш код в файл, который я назвал pimp.c из-за отсутствия лучшего имени). Тогда вы получите следующее:

$ gcc pimp.c
pimp.c: In function `main':
pimp.c:9: error: `xi' undeclared (first use in this function)
pimp.c:9: error: (Each undeclared identifier is reported only once
pimp.c:9: error: for each function it appears in.)

Короче говоря, нет, вы не можете этого сделать. Чтобы иметь возможность сделать именно это, препроцессор должен действовать как интерпретатор. C и C++ (обычно) неинтерпретируемые языки, а препроцессор не является интерпретатором. Мое предложение состояло бы в том, чтобы четко определить различия между компиляторами и интерпретаторами (и между компилируемыми и интерпретируемыми языками).

С уважением.

person luis.espinal    schedule 16.02.2011