Есть ли веская причина всегда заключать определение в скобки в C?

Понятно, что бывают случаи, когда defines должны иметь круглые скобки, вот так:

#define WIDTH 80+20

int a = WIDTH * 2; //expect a==200 but a==120

Поэтому я всегда заключал в скобки, даже если это просто одно число:

#define WIDTH (100)

Кто-то новичок в C спросил меня, почему я делаю это, поэтому я попытался найти крайний случай, когда отсутствие скобок на одном числе define вызывает проблемы, но я не могу думать об одном.

Существует ли такой случай?

9 ответов

Решение

Да Оператор конкатенации препроцессора (##) вызовет проблемы, например:

#define _add_penguin(a) penguin ## a
#define add_penguin(a) _add_penguin(a)

#define WIDTH (100)
#define HEIGHT 200    

add_penguin(HEIGHT) // expands to penguin200
add_penguin(WIDTH)  // error, cannot concatenate penguin and (100) 

То же самое для строк (#). Очевидно, что это угловой случай и, вероятно, не имеет значения, учитывая, как WIDTH будет предположительно использоваться. Тем не менее, есть кое-что, что нужно иметь в виду о препроцессоре.

(Причина, по которой при добавлении второго пингвина происходит сбой, является тонкой деталью правил предварительной обработки в C99 - iirc не работает, потому что конкатенация с двумя токенами предварительной обработки без заполнителей всегда должна приводить к одному токену предварительной обработки - но это не имеет значения, даже если конкатенация было разрешено, это все равно даст другой результат, чем без скобок #define!).

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

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

Прямо сейчас ваш макрос представляет собой одно целое число. Вообразите, что кто-то редактирует это в будущем. Допустим, это не вы, а тот, кто менее осторожен или более спешит. Скобка предназначена для того, чтобы напомнить им о внесении любых изменений в скобки.

Такое мышление является хорошей привычкой в ​​C. Я лично пишу код в стиле, который некоторые люди могут найти "избыточным", с такими вещами, но особенно в отношении обработки ошибок. Избыточность предназначена для обеспечения возможности сопровождения и компоновки будущих правок.

Когда код определяет только число, @Alexander Gessler хорошо отвечает на вопрос.

Однако многие кодеры не замечают унарные операторы в следующем:

#define TEMPERATURE1M (-1)
#define TEMPERATURE1P (+1)

Когда код использует #define который нанимает оператора, включая () обеспечивает ожидаемые числовые результаты и приоритет.

#define TEMPERATURE_WITH  (-1)
#define TEMPERATURE_WITHOUT -1

// Consider how these will compile
int w  = 10-TEMPERATURE_WITH;
int wo = 10-TEMPERATURE_WITHOUT;  // May not compile

Последняя строка кода может компилироваться с учетом семантических изменений C99 @Olaf

Как сказал Благовест Буюклиев:

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

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

  1. Избегайте таких функций, как макросы @ см. Комментарий Лундина.

Если вы хотите использовать такие функции, как макросы, плохо учитывайте следующие 2 правила:

  1. Всегда используйте скобки для аргументов в макросах
  2. Используйте макрос аргумент только один раз

Почему правило 1.? (Чтобы сохранить порядок операций правильно)

#define quad(x) (x*x)
int a = quad(2+3);

будет расширяться до:

int a = (2+3*2+3);

Почему правило 2.? (Чтобы гарантировать, что побочный эффект применяется только один раз)

#define quad(x) (x*x)
int i = 1;
int a = quad(i++);

будет расширяться до:

int a = i++ * i++;

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

Поскольку 100 это один токен, я сомневаюсь, что вы найдете угловой случай, когда скобки имеют значение (для одного токена!)

Это все еще хорошая привычка IMO, так как они могут иметь значение, когда задействовано несколько токенов.

Нет ни одного случая, когда #define WIDTH 100 может привести к однозначному или "удивительному" расширению. Это потому, что это может привести только к замене одного токена одним токеном.

Как вы знаете, макрос путаницы возникает, когда один токен (например, WIDTH) приводит к нескольким токенам (например, 80 + 20). Насколько я могу догадаться, это единственная причина использования скобок в подстановках, и, как показано в моем первом абзаце, здесь это не применимо.

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

Иногда есть веская причина.

Для одного номера нет веской причины.

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

Некоторые люди предпочитают быть очень осторожными и всегда использовать скобки (@aix рекомендует. Я не знаю, но нет точного ответа).

Это, конечно, не повредит, и это хорошая привычка. Но нет никакой разницы между (100) а также 100 для численных расчетов.

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