3

I have some short defines in one of my headers like this:

#define ROUND_DOWN(a,b) (a)-(a)%(b) 

e.g.

ROUND_DOWN(178,32) = 160 

But if I pass this to it:

ROUND_DOWN(160*2, 32); 

then it gets compiled like this?

(160*2)-(160*2)%(32), 

which is just more processing as it does 160*2 twice..

I'm wondering if inline functions behave in the same way? e.g.

inline int RoundDown(int a, int b) { return (a)-(a)%(b) } 

Would 160*2 get stored in "int a" as 320 and then the calculation would work, or would it behave the same as the define?

A better example is calling:

RoundDown((x+x2)*zoom, tile_width); 
3
  • 2 * ROUND_DOWN(178, 32) will make things not work anymore. Use #define ROUND_DOWN(a,b) ((a)-(a)%(b)) instead (note the extra parentheses). Conclusion: won't behave the same anyway. Commented Jul 5, 2011 at 14:57
  • Try ROUND_DOWN(x++, y) with your macro and see what happens. Commented Jul 5, 2011 at 15:03
  • 2
    @Fred: Or don't see what happens, since UB can cause invisible demons to fly out of your nose. Commented Jul 5, 2011 at 15:08

5 Answers 5

8

Do “#define” and inline behave the same?

No they dont!

There are a number of differences between a macro and a inline function.

- No of times of Evaluation

Expressions passed as arguments to inline functions are evaluated once.

In some cases, expressions passed as arguments to macros can be evaluated more than once. Every time you use an argument in a macro, that argument is evaluated.

A Code sample:

#define max(a,b) (a>b?a:b) int main() { int a = 0; int b = 1; int c = max(a++, b++); cout << a << endl << b << endl; return 0; } 

The intention probably was to print 1 and 2, but macro expands to:

int c = a++ > b++ ? a++ : b++; 

b gets incremented twice, and the program prints 1 and 3.

- Who evaluates them

Inline functions are evaluated by the compiler while Macros are evaluated at pre-compilation by precompiler.

- Type checking

Inline functions follow all the protocols of type safety enforced on normal functions. Argument types are checked, and necessary conversions are performed correctly. The compiler performs return type checking, function signature before putting inline function into symbol table.
They can be overloaded to perform the right kind of operation for the right kind of data.

Macros are more error prone as compared to inline functions. the The parameters are not typed (the macro works for any objects of arithmetic type). No error checking is done during compilation.

A Code Sample:

#define MAX(a, b) ((a < b) ? b : a) int main( void) { cout << "Maximum of 10 and 20 is " << MAX("20", "10") << endl; return 0; } 

One can pass strings to a macro that does some integer arithmetic and a macro won't complain!

- Suggestion or Command?

Inline is just a suggestion to the compiler. It is the compiler’s decision whether to expand the function inline or not.

Macros will always be expanded.

- How about Debugging?

Inline functions can be debugged easily because you can put a break point at the inline function definition and step into the method for debugging step by step.

Macros can not be used for debugging as they are expanded at pre-compile time.

Sign up to request clarification or add additional context in comments.

4 Comments

Your MAXdefines are broken. Try MAX(5 & 3, 3 & 3), it won't give the expected result 3 but 1.
As a simple rule, when you define a macro, always put parenthesis around your parameters, always!
@tristopia: The code examples are pointing out evils macros cause if not used correctly, your comment points the samething out, they are not meant to teach how macros should be written.
May be, but the OP's question was the difference between macros and inlines. The error cases listed in your post did address some of the problems with macros, the one I pointed out is not addressed in your answer. And btw, I didn't downvote your post.
5

First, you should pretty much assume that all constant expressions are evaluated at compile-time, so that multiplication never survives to be executed when you run the program.

Second, you can't depend on inline having any effect at all, it's just a hint to the compiler, not a requirement.

But even if the function is not inlined, the expression would not be evaluated twice since argument passing requires it to be evaluated before the body of the function runs.

2 Comments

sorry, my bad example, i don't do constant calculations, my code is more like "RoundDown((x+x2)*zoom, tile_width);", if the compiler did see my function as inline, would it calculate twice or store in the argument?
@Kaije: The expression will be evaluated once. Period. The contents of a function, inline or not, has no bearing whatsoever on how many times its parameters are evaluated.
3

#defines are simple textual substitutions, so (as you noticed) you may need to be careful with parentheses, etc. inline parameters are parsed normally.

There's a related issue with respect to conditions.

Comments

2

Nominally, the function argument 160*2 is evaluated exactly once, and the result is then used in the body of the function, whereas the macro evaluates 160*2 twice. If the argument expression has side-effects, then you can see this[*]: ROUND_DOWN(printf("hi!\n"), 1); vs RoundDown(printf("hi!\n"), 1);

In practice, whether the function is inlined or the macro expanded, it's just integer arithmetic in the expression, with no side-effects. An optimizing compiler can work out the result of the whole macro/function call, and just stick the answer in the emitted code. So you might find that your macro and your inline function result in exactly the same code being executed, and so int a = ROUND_DOWN(160*2, 32); and int a = RoundDown(160*2, 32); might both be the same as int a = 320;.

Where there are no side-effects, optimization can also store and re-use intermediate results. So int c = ROUND_DONW(a*2, b); might end up emitting code that looks as though you've written:

int tmp = a*2; int c = tmp - tmp % b; 

Note that whether to actually inline a function is a decision made by the compiler based on its own optimization rules. Those rules might take account of whether the function is marked inline or not, but quite likely don't unless you're using compiler options to force inlining or whatever.

So, assuming a decent compiler there's no reason to use a macro for this - for your macro in particular you're just begging for someone to come along and write:

int a = ROUND_DOWN(321, 32) * 2; 

and then waste a few minutes wondering why the result is 319.

[*] Although don't get carried away - for some expressions with side-effects, for example i++ where i is an integer, the macro has undefined behavior due to lack of sequence points.

Comments

1

Well in the example you've given with constants, on any reasonable compiler both versions will compute the constant at compile time.

Assuming you're actually asking about cases where variables are passed in, I would expect the compiler's optimizer to generate the same code in both cases (it wouldn't do the multiplication twice if it's more efficient to save off the result. Finally, the inline function does give the compiler the option to make an actual function call if it would improve performance.

Finally note that I wouldn't worry about micro-optimizations like this because 99% it's just going to have no effect on the performance of your program - I/O will be your bottleneck.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.