2013-02-22 2 views
3

Я начал читать Эффективное использование C++ и в какой-то момент в пункте 2, следующее упоминается:функции, как макросы и странное поведение

// call f with the maximum of a and b 
#define CALL_WITH_MAX(a, b) f((a) > (b) ? (a) : (b)) 

... 

int a = 5, b = 0; 
CALL_WITH_MAX(++a, b); // a is incremented twice 
CALL_WITH_MAX(++a, b+10); // a is incremented once 

Здесь количество раз, которое увеличивается до вызова f зависит от того, с чем он сравнивается!

В самом деле, если я использую простой оператор печати в f, 7 печатается в первом вызове, но я не могу за жизнь мне понять, почему. Мне что-то не хватает?

+2

@nhahtdh Не просто «думать о», макросы * делать * простая замена текста. Со многими компиляторами (например, g ++) вы можете попросить просмотреть код после прохождения препроцессора. – dmckee

ответ

5

Компилятор заменяет макросы именно тем, что вы передаете, дословно. Таким образом, вы в конечном итоге с

int a = 5, b = 0; 
f((++a) > (b) ? (++a) : (b)); 
f((++a) > (b+10) ? (++a) : (b+10)); 
+3

Мораль этой истории: * никогда не ссылайтесь на макро-аргумент более одного раза внутри тела макроса, иначе будут происходить плохие вещи *. –

+3

@NikBougalis - мораль никогда не применяет макрос к аргументу, который имеет побочные эффекты *. –

+0

Это, пожалуй, лучший способ положить это. –

1

Использование g++ -E myprog.cpp (замените g++ с whatever-your-compiler-is, если вы не используете g++) - он работает почти на всех компиляторов, он будет производить фактический материал после предварительной обработки.

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

Вы бы получить гораздо больше того, что вы (вероятно) ожидать, если вы должны были использовать встроенную функцию:

inline void CallWithMax(int a, int b) 
{ 
    f((a) > (b) ? (a) : (b)); 
} 

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

Вы также можете выполнить встроенную функцию, если вы создаете код с помощью отладочных символов, поэтому, если вы хотите увидеть, какое значение a и b действительно находятся внутри функции, вы можете это сделать. Макросы, потому что они расширяются в исходное место в исходном коде, поэтому вы не можете увидеть, что происходит внутри.

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