Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Which one of the following macros is safe and why?

For getting maximum of two numbers i have following macros

#define max(a,b) ((a) > (b) ? (a) : (b))

#define maxint(a,b) ({int _a = (a), _b = (b); _a > _b ? _a : _b; })

What is the difference between above both. which one is better to use and why. I found these macros' information here. But unable to understand it.

like image 437
Chinna Avatar asked Jan 23 '14 06:01

Chinna


3 Answers

The second macro is safer, but uses a non-standard C extension provided by GCC : statement expressions. But the first expression is "generic". Using the typeof extension of GCC would help:

 #define mymax(a,b) ({typeof(a) _a = (a); typeof(b) _b = (b); \
                      _a > _b ? _a : _b; })

To fight against the issue raised in JaredPar's answer you could use preprocessor concatenation and GCC specific __COUNTER__ (or just the more standard __LINE__), e.g.

 #define mymax_counted(a,b,c) ({typeof(a) _a##c = (a); \
                                typeof(b) _b##c = (b); \
                                _a##c > _b##c ? _a##c : _b##c; })
 #define mymax(a,b) mymax_counted(a,b,__COUNTER__)

but even that is not entirely fail-proof (with bad luck; could collide in some invocation like mymax(_a123,_b123) if the unique __COUNTER__ happens to be 123 at that time).

Actually, using an inline function is better (because if you call mymaxfun(i++,t[i]) the behavior is well defined and gives the same result as mymaxfun(t[i],i++), and because optimizing compilers would produce code as efficient as when using a macro):

static inline int mymaxfun(int a, int b) { return (a>b)?a:b; }

Sadly, C don't have generic functions (consider for that switching to C++ with its templates and use std::max); however C11 has type-generic expressions using the _Generic keyword

Macros are useful (when mastered), but you should be very careful about side effects in arguments when calling macros (e.g. mymax(i++,t[--i]++)) so you always should care and document if a name is a macro or something else (like a function). As a rule of thumb avoid side-effects -notably ++ or -- but also many others- in function-call looking expressions (both function calls and macro invocations).

Look at the preprocessor-expanded form of your source code; so for a foo.c source code, run gcc -C -E foo.c > foo.i (adding whatever preprocessor options like -I, -D etc... are relevant) and look inside foo.i e.g. with less foo.i; it is always instructive.

like image 75
Basile Starynkevitch Avatar answered Oct 11 '22 03:10

Basile Starynkevitch


The second macro is safer because it evaluates the inputs exactly once. This is important if the inputs are expressions that have side effects for example

max(i++, --j); 

Note that i said safer and not safe. It's still possible for this macro to be incorrect because there could be locals in scope which are already named _a and _b. Imagine what would happen if the following was executed

int _a = 42;
int _b = 13;
maxint(_a, _b);

It would expand out to

int _a = 42;
int _b = 13;
{int _a = (_a), _b = (_b); _a > _b ? _a : _b; })
like image 42
JaredPar Avatar answered Oct 11 '22 03:10

JaredPar


Neither are safe. Please avoid flashy max macro implementations that rely on non-standard C extensions like expression statements which will give you all sort of headaches if you ever have to port your code to a different platform. Doing my best to present this answer from becoming a rant, the max macro is, in my opinion, one of the worst things in C standard libraries due to its potential side-effects. In times past, I've #undefed max just to be on the safe side.

Your're better off coding the second macro maxint as a function as it has all the drawbacks of macros (e.g. can't debug easily, instantiated variables clashing with locals) but none of the benefits (e.g. genericisation).

Alternatives:

My favourite: use the ternary inline: a > b ? a : b where you can drop the parentheses to taste since you know exactly what is going on. It will also be faster than macros that try to be safe by taking value copies.

Build your own functions. Consider defining max for integral types and fmax for floating point. There is already a precedent here with abs and fabs.

like image 34
Bathsheba Avatar answered Oct 11 '22 04:10

Bathsheba