mcdaniel@uicsrd.csrd.uiuc.edu (11/04/88)
Written 1:43 pm Nov 1, 1988 by knudsen@ihlpl.ATT.COM in comp.lang.c: > In article <353@marob.MASA.COM>, daveh@marob.MASA.COM (Dave Hammond) writes: >> #define FOO() do { foo1(); foo2(); foo3() foo4(); } while(0) > Why use the do and while? C has blocks, you can say just plain > {foo1(); ... foo4();} Try compiling this code: #include <stdio.h> #define FOO() { printf("Hello, "); printf("world!\n"); } main() { if (1) FOO(); /* line 8 */ else /* line 9 */ exit(1); exit(0); } If your compiler is like mine (PCC, on a VAX, under BSD 4.3), it will reply: "t.c", line 9: syntax error According to K&R, first edition, the syntax of the "if" statement is if ( <expression> ) <statement> else <statement> and <statement> is ; (the null statement) or { <declaration-list-opt> <statement-list-opt> } (or "for ...", "while ...", "<expression> ;", et cetera) Thus if (1) { } ; else ; is illegal. "{ }" is one statement, and ";" is another (a null statement), and you can have only one statement between an "if" and its "else". One choice is to remove the ";" on line 8. Then it looks like a function call without a ";" after it, which looks weird. Thus, the usual way to define such a FOO() is #define FOO() do { foo1(); foo2(); foo3() foo4(); } while(0) or #define FOO() if (1) { foo1(); foo2(); foo3() foo4(); } else NOTE the "else" in the second case. If it were omitted, if (x) FOO(); else BAR(); would cause a syntax error, and if the semicolon were removed, if (x) FOO() else BAR(); the "else" pairs with the "if" in FOO, NOT the "if (x)". The problem with THESE solutions is that "lint -h" under BSD 4.3 starts spitting out messages like t.c(7): warning: constant in conditional context t.c(8): warning: constant argument to NOT You DO lint all your code, don't you? 8-( To remove these messages, I use a common ".h" file with #ifdef lint static int true = 1; static int false = 0; #else #define true 1 #define false 0 #endif and use "do ... while (false)" and "if (true) ... else". All for the lack of an "inline" keyword in standard C. *sigh* A better definition is >> #define FOO() foo1(), foo2(), foo3(), foo4() because it can be used in expressions. However, you should use parentheses around macro text! Consider bar(FOO()) You expect bar() to be called with one argument, the value of foo4(), but instead bar() is called with 4 arguments. (Lint catches most errors of this kind, which means most programmers will never notice this error until it causes the program to core dump. 8-( ) In general, when you write macros that evaluate to expressions, you should parenthesize the result, as above. By the way, you should also parenthesize all uses of macro arguments. Consider #define two_it(x) (2*x) The whole thing is parenthesized, which is as suggested above. However, if it is called as "two_it(x+y)", this call becomes "(2*x+y)", which is probably NOT what you expected. #define two_it(x) (2*(x)) works better. -- Tim, the Bizarre and Oddly-Dressed Enchanter Center for Supercomputing Research and Development at the University of Illinoid at Urbana-Champaign Internet, BITNET: mcdaniel@uicsrd.csrd.uiuc.edu UUCP: {uunet,convex,pur-ee}!uiucuxc!uicsrd!mcdaniel ARPANET: mcdaniel%uicsrd@uxc.cso.uiuc.edu CSNET: mcdaniel%uicsrd@uiuc.csnet DECnet?: GARCON::"mcdaniel@uicsrd.csrd.uiuc.edu"
davidsen@steinmetz.ge.com (William E. Davidsen Jr) (11/12/88)
In article <44200018@uicsrd.csrd.uiuc.edu> mcdaniel@uicsrd.csrd.uiuc.edu writes: | A better definition is | >> #define FOO() foo1(), foo2(), foo3(), foo4() | because it can be used in expressions. However, you should use | parentheses around macro text! Consider | bar(FOO()) | You expect bar() to be called with one argument, the value of foo4(), | but instead bar() is called with 4 arguments. (Lint catches most | errors of this kind, which means most programmers will never notice | this error until it causes the program to core dump. 8-( ) | | In general, when you write macros that evaluate to expressions, you | should parenthesize the result, as above. Somewhere the "as above" with correct parens seems to have gotten lost. I'm sure what you meant was to state the example: #define FOO() (foo1(), foo2(), foo3(), foo4()) as you explain in your text. I'm afraid that some readers may look at the example and miss your points about macros in procedure calls, etc. | | By the way, you should also parenthesize all uses of macro arguments. | Consider | #define two_it(x) (2*x) | The whole thing is parenthesized, which is as suggested above. | However, if it is called as "two_it(x+y)", this call becomes | "(2*x+y)", which is probably NOT what you expected. | #define two_it(x) (2*(x)) | works better. | | -- | Tim, the Bizarre and Oddly-Dressed Enchanter | Center for Supercomputing Research and Development | at the University of Illinoid at Urbana-Champaign | | Internet, BITNET: mcdaniel@uicsrd.csrd.uiuc.edu | UUCP: {uunet,convex,pur-ee}!uiucuxc!uicsrd!mcdaniel | ARPANET: mcdaniel%uicsrd@uxc.cso.uiuc.edu | CSNET: mcdaniel%uicsrd@uiuc.csnet | DECnet?: GARCON::"mcdaniel@uicsrd.csrd.uiuc.edu" -- bill davidsen (wedu@ge-crd.arpa) {uunet | philabs}!steinmetz!crdos1!davidsen "Stupidity, like virtue, is its own reward" -me