13

How can I write a for/while loop inside a #define directive in C?

a3f
  • 8,517
  • 1
  • 41
  • 46
hari
  • 9,439
  • 27
  • 76
  • 110
  • 6
    Do you think that's a good idea? What's wrong with a short function? For instance, a function won't have to leak an iteration variable into the scopes that use it... –  Apr 22 '11 at 20:42
  • @delnan: While I agree that functions should be preferred to macros in almost all cases, a for loop's declaration (the first part) is required to not be leaked outside. – Thomas Edleson Apr 22 '11 at 20:47
  • 3
    @Thomas Edleson: Thinking about it, yes, you're right. Even in older language versions where you can't do `for (int i = 0; ...)`, wrapping it in `do { } while (0)` creates a new scope anyway. –  Apr 22 '11 at 20:52
  • @delnan: Why not just wrap it in `{...}`; the `do/while` is superfluous. – Lawrence Dol Apr 22 '11 at 20:57
  • 2
    @Software Monkey: See http://stackoverflow.com/questions/154136/why-are-there-sometimes-meaningless-do-while-and-if-else-statements-in-c-c-macr –  Apr 22 '11 at 21:02
  • @delnan: Ahhh. Forgot about that particular wrinkle with C - I knew there were good reasons why we require all if/else clauses to use braces - this would be one of them. – Lawrence Dol Apr 22 '11 at 21:11

6 Answers6

14

You're probably looking for \ to continue a macro definition across several lines:

#define LOOP(start, end)                  \
  for (int i = (start); i < (end); i++) { \
    printf("%d\n", i);                    \
  }
Thomas Edleson
  • 2,175
  • 1
  • 16
  • 19
JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
13

Short answer is "don't". But if you have to, for the love of all that's sacred don't do this:

#define FOREACH(start, end)                         \
      for (; (start) < (end); (start)++)            \
      {                                             \
        // do something interesting                 \
      }                                             

Bad juju all the way around. Note that start must correspond to an lvalue; you would not be able to call this as FOREACH(1,10), or FOREACH((a+b), c), or FOREACH(x++,y++). All of those would lead to a compile-time error (the operand of ++ must be an lvalue, and none of 1, a+b, or x++ qualify). Calling it as FOREACH(x, y++) will do something you really don't want it to do. Similarly, you wouldn't want to call it as FOREACH(x, y()).

You can guard against these problems to an extent by doing something like

#define FOREACH(start, end)                        \
do {                                               \
  int i;                                           \
  int j = end;                                     \
  for (i = start; i < j; i++)  {                   \ 
    // do something interesting                    \
  }                                                \
} while (0)

Essentially, you're creating local variables corresponding to your macro arguments. This protects against start not being an lvalue, and against end having a side effect that gets applied or being a function that gets called every iteration.

But if you're trying to encapsulate a loop that gets called frequently, put it in its own separate function. It's safer and easier to understand and maintain.

John Bode
  • 119,563
  • 19
  • 122
  • 198
  • do `lvalue`s exist in C? I thought that was only a C++ thing? – Coder Feb 04 '23 at 21:10
  • @Coder: Yes, lvalues are a thing in C - "An *lvalue* is an expression (with an object type other than `void`) that potentially designates an object;" - C 202x working draft, 6.3.2.1. – John Bode Feb 05 '23 at 03:19
5

Since C doesn't require statements to be on separate lines, you can simply smush together into one long line:

#define M while (...) { ...; ...; }

Or you could escape newlines in the macro definition:

#define M \
  while (...) { \
    ...; \
    ...; \
  }
Thomas Edleson
  • 2,175
  • 1
  • 16
  • 19
4
#define something for(;;) printf("hooray, i'm in infinite loop!");


int main() { something }
nothrow
  • 15,882
  • 9
  • 57
  • 104
2

This would be a more generic for loop

#include <stdio.h>
#include <string.h>

#define for_loop(start, end, incr) for(i = start; \
                                        i < end;  \
                                         i += incr)

int main()
{
  int i=0, j=5;

  for_loop(i, j, 1)
    printf("%d\n", i+1);

  return 0;
}
Vedang Joshi
  • 149
  • 1
  • 2
  • 7
  • Very concise while still human-readable, and it's extremely versatile (unlike the accepted answer which requires different declarations for different executions). Well done! – 255.tar.xz Jan 15 '20 at 01:18
2
#define foo(x) do { \
    for(x=0;x<4;x++) x; \
    } while(0) // note lack of trailing ;

or in gnu c:

#define foo(x) ({ \
    for(x=0;x<4;x++) x; \
    })

The latter can be used as a expression, although this one has type void, and thus is not very useful.

David X
  • 3,998
  • 3
  • 29
  • 32
  • Don't you have a conflict between the macro parameter `x` and the control variable `x` in the for loop? And why the `do/while(0)`? – Lawrence Dol Apr 22 '11 at 20:55
  • @Software Monkey, the macro parameter `x` is the control variable, I wanted to keep it very simple. The `do`/`while(0)` is to ensure that it isn't accidentally used as eg `foo(i) += 12;` or something else that the compiler would likely happily allow. – David X Apr 22 '11 at 20:57
  • 2
    Huh? So `foo(index)` tries to execute the statement `index` 4 times, using `index` as the control counter variable? – Lawrence Dol Apr 22 '11 at 20:59
  • Pretty much, it's not very useful. – David X Apr 22 '11 at 21:00
  • 2
    @Software Monkey: questioner didn't specify what the loop should actually do, presumably that can be filled in later. – Steve Jessop Apr 22 '11 at 21:09