Take the 2-minute tour ×

Can you tell me if anything and what can go wrong with this C "function macro"?

#define foo(P,I,X) do { (P)[I] = X; } while(0)

My goal is that foo behaves exactly like the following function foofunc for any POD data type T (i.e. int, float*, struct my_struct { int a,b,c; }):

static inline void foofunc(T* p, size_t i, T x) { p[i] = x; }

For example this is working correctly:

int i = 0;
float p;

It can handle things like &p due to putting P in parentheses, it does increment i exactly once because I appears only once in the macro and it requires a semicolon at the end of the line due to do {} while(0).

Are there other situations of which I am not aware of and in which the macro foo would not behave like the function foofunc?

In C++ one could define foofunc as a template and would not need the macro. But I look for a solution which works in plain C (C99).

share|improve this question
Out of curiosity, why would you want to use a macro when foofunc will be inlined? There wouldn't be any performance benefit despite running risks with pointers. –  Dai Dec 27 '13 at 1:50
foofunc only works for exactly on type T, thus one needs one definition per type. If I want foofunc for int, float* and my_struct I would need foofunc_int, foofunc_floatptr, foofunc_my_struct. The macro works with any type. –  Danvil Dec 27 '13 at 1:57
That's what templates are for. Just stick template <typename T> before your function definition and it'll work like you want it to. –  parkovski Dec 27 '13 at 2:02
@parkovski: This question is about C, not C++. So no templates :( –  Danvil Dec 27 '13 at 2:03
@OliCharlesworth mentions a good point with weird parameters actually getting compiled. If you pedantically parenthesized then that should fix all the possible parameter issues (at least the ones I can imagine) (i.e. have your macro be (P)[(I)] = (X)). –  Cornstalks Dec 27 '13 at 2:37

2 Answers 2

up vote 2 down vote accepted

The fact that your macro works for arbitrary X arguments hinges on the details of operator precedence. I recommend using parentheses even if they happen not to be necessary here.

#define foo(P,I,X) do { (P)[I] = (X); } while(0)

This is an instruction, not an expression, so it cannot be used everywhere foofunc(P,I,X) could be. Even if foofunc returns void, it can be used in comma expressions; foo can't. But you can easily define foo as an expression, with a cast to void if you don't want to risk using the result.

#define foo(P,I,X) ((void)((P)[I] = (X)))

With a macro instead of a function, all you lose is the error checking. For example, you can write foo(3, ptr, 42) instead of foo(ptr, 3, 42). In an implementation where size_t is smaller than ptrdiff_t, using the function may truncate I, but the macro's behavior is more intuitive. The type of X may be different from the type that P points to: an automatic conversion will take place, so in effect it is the type of P that determines which typed foofunc is equivalent.

In the important respects, the macro is safe. With appropriate parentheses, if you pass syntactically reasonable arguments, you get a well-formed expansion. Since each parameter is used exactly once, all side effects will take place. The order of evaluation between the parameters is undefined either way.

share|improve this answer
Good point with the comma expressions! int i; int*p=&i; if(foo(p,0,5), i==5) { /**/ } would only work for foofunc, not for foo. –  Danvil Dec 27 '13 at 13:28

The do { ... } while(0) construct protects your result from any harm, your inputs P and I are protected by () and [], respectively. What is not protected, is X. So the question is, whether protection is needed for X.

Looking at the operator precedence table (http://en.wikipedia.org/wiki/Operators_in_C_and_C%2B%2B#Operator_precedence), we see that only two operators are listed as having lower precedence than = so that the assignment could steal their argument: the throw operator (this is C++ only) and the , operator.

Now, apart from being C++ only, the throw operator is uncritical because it does not have a left hand argument that could be stolen.
The , operator, on the other hand, would be a problem if X could contain it as a top level operator. But if you parse the statement

foo(array, index, x += y, y)

you see that the , operator would be interpreted to delimit a fourth argument, and

foo(array, index, (x += y, y))

already comes with the parentheses it requires.

To make a long story short:
Yes, your definition is safe.

However, your definition relies on the impossibility to pass stuff, more_stuff as one macro parameter without adding parentheses. I would prefer not to rely on such intricacies, and just write the obviously safe

share|improve this answer

Your Answer


