конфликт макросов и функций в C - PullRequest
4 голосов
/ 19 февраля 2010

Какая ошибка возникает при возникновении конфликта макросов и функций в C?Это ошибка макропроцессора или эта ошибка возникает из-за нарушения языка?

Например, в этом коде:

#include <stdio.h>
#define MAX(i, j) (i>j)? i : j

int MAX(int i, int j){
  return (i>j) ? i : j;
}

int main(){
  int max = MAX(5, 7);
  printf("%d",max);
  return 0;
}

Программа выдает ошибку времени компиляции.Но я не понимаю, было ли это какое-то нарушение языка, ошибка расширения макроса или что-то еще.

Ответы [ 6 ]

11 голосов
/ 19 февраля 2010

На этапе предварительной обработки код преобразуется в:

int (int i>int j)? int i : int j{
  return (i>j) ? i : j;
}

int main(){
  int max = (5>7)? 5 : 7;
  printf("%d",max);
  return 0;
}

... что, как любой может сказать, является недопустимым кодом C.

gcc вы можете использовать опцию -E, чтобы увидеть предварительно обработанную версию файла.)

3 голосов
/ 19 февраля 2010

Другие указали на проблему, но не дали решения.Лучше всего было бы просто использовать MAX как встроенную функцию.Если макрос находится в заголовке, поместите вместо него определение встроенной функции.Если вы все еще хотите использовать макрос (или использовать старый компилятор C, который не поддерживает встроенные функции), вы можете определить функцию следующим образом:

int (MAX)(int i, int j){
    return (i>j) ? i : j;
}

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

extern int (MAX)(int, int);
...
int (*max_func_ptr)(int, int);
...
max_func_ptr = MAX;
2 голосов
/ 19 февраля 2010

Нет ошибки, которая возникает специально для вызова конфликта. Что происходит, так это то, что обработка макроса происходит в первую очередь, поэтому в определении макроопределений не учитывается пространство имен - это одна из основных причин, по которой макросы считаются неправильными, и их следует избегать, кроме как в крайнем случае. См. Этот вопрос для примера ситуации, когда чье-то совершенно правильное использование имени BitTest для шаблона было испорчено, потому что кто-то другой решил создать макрос, использующий это имя для псевдонима другого имени: Win32: BitTest , BitTestAndComplement, ... <- Как отключить этот мусор? </a>

Итак, в вашем примере, как уже упоминалось в других ответах, после того, как будет выполнен шаг предварительной обработки, вы получите что-то вроде следующего фрагмента, передаваемого компилятору C вместо определения вашей функции:

int (int i>int j)? int i : int j{
  return (i>j) ? i : j;
}

Это недопустимый C, поэтому вы получите сообщение об ошибке компилятора, которое может сказать (из GCC 3.4.5):

error: syntax error before "int"

или (из MSVC 9):

error C2059: syntax error : 'type'

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

int MAX(int i, int j){

, который выглядит действительным.

Существует несколько методов, которые помогают избежать этой проблемы:

  • использовать все заглавные буквы для имен макросов и только для имен макросов; это соглашение, которое в основном соблюдается, чтобы отделить пространство имен макросов от имен, используемых для других вещей

  • ставить символы «скобки» вокруг имен, которые вы не хотите раскрывать как макросы (это работает только для «функционально-подобных» макросов). Если ваш пример был написан следующим образом:

    int (MAX)(int i, int j){
      return (i>j) ? i : j;
    }
    

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

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

    Вместо этого используйте встроенные функции или шаблоны функций.

2 голосов
/ 19 февраля 2010

Вы получите ошибку компилятора, потому что макрос будет раскрыт перед попыткой компиляции.

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

1 голос
/ 19 февраля 2010

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

int (int i>int j)? int i : int j

, что недействительно C.

1 голос
/ 19 февраля 2010

Поскольку макросы препроцессора используются только для обработки простого текста, эти конфликты всегда приводят к ошибке времени компиляции.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...