Есть ли веская причина всегда заключать определение в скобки в C? - PullRequest
48 голосов
/ 31 января 2012

Очевидно, что в некоторых случаях операторы #define должны иметь круглые скобки, например:

#define WIDTH 80+20

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

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

#define WIDTH (100)

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

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

Ответы [ 9 ]

37 голосов
/ 31 января 2012

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

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

26 голосов
/ 31 января 2012

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

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

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

8 голосов
/ 31 января 2012

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

Определение состоит из одного токена (только один операнд, без операторов), скобки не нужны, потому что один токен (например, 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++;
8 голосов
/ 31 января 2012

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

6 голосов
/ 31 января 2012

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

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

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

6 голосов
/ 31 января 2012

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

3 голосов
/ 10 февраля 2016

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

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

#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изменения @ Олаф

3 голосов
/ 31 января 2012

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

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

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

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

2 голосов
/ 31 января 2012

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

...