Since the value of a comma operator statement is the last comma separated expression in the statement you can use this to perform several expressions while keeping the value only of the last expression.
For instance, lets say that you want to have a macro that will do several calculations but the value of the macro is the last calculation. The following macro takes advantage of the assignment operator being used in an expression to perform a calculation and allow a different calculation to be the result of the macro:
#define SYMVAR(a,b) ( ((a)=(b)), (b)*2)
This could also be a sequence of function calls such as:
int f1(int x) { return x * 3; } int f2(int x) { return x * 4; } // call first function f1() and then f2() with the value of the macro being the // value returned by function f2(). #define SYMVARy(a) (f1(a), f2(a))
An example of using this approach:
#include <stdio.h> #define SYMVAR(a,b) ( ((a)=(b)), (b)*2) #define SYMVAR2(a,b,z) ( ((a)=(b)), ((z)=(b))*2) int main() { int n1, n2; int x1, x2; int y0; int i; for (i = 0; i < 10; i++) { printf(" i = %d -> ", i); x1 = SYMVAR(n1, i); printf(" n1 = %d, i = %d, x1 = %d\n", n1, i, x1); } printf("\nloop 2\n"); for (i = 0; i < 10; i++) { printf(" i = %d -> ", i); x2 = SYMVAR2(n2, i+3, y0); printf(" n2 = %d, i = %d, x2 = %d, y0 = %d\n", n2, i, x2, y0); } return 0; }
The output of this program is:
i = 0 -> n1 = 0, i = 0, x1 = 0 i = 1 -> n1 = 1, i = 1, x1 = 2 i = 2 -> n1 = 2, i = 2, x1 = 4 i = 3 -> n1 = 3, i = 3, x1 = 6 i = 4 -> n1 = 4, i = 4, x1 = 8 i = 5 -> n1 = 5, i = 5, x1 = 10 i = 6 -> n1 = 6, i = 6, x1 = 12 i = 7 -> n1 = 7, i = 7, x1 = 14 i = 8 -> n1 = 8, i = 8, x1 = 16 i = 9 -> n1 = 9, i = 9, x1 = 18 loop 2 i = 0 -> n2 = 3, i = 0, x2 = 6, y0 = 3 i = 1 -> n2 = 4, i = 1, x2 = 8, y0 = 4 i = 2 -> n2 = 5, i = 2, x2 = 10, y0 = 5 i = 3 -> n2 = 6, i = 3, x2 = 12, y0 = 6 i = 4 -> n2 = 7, i = 4, x2 = 14, y0 = 7 i = 5 -> n2 = 8, i = 5, x2 = 16, y0 = 8 i = 6 -> n2 = 9, i = 6, x2 = 18, y0 = 9 i = 7 -> n2 = 10, i = 7, x2 = 20, y0 = 10 i = 8 -> n2 = 11, i = 8, x2 = 22, y0 = 11 i = 9 -> n2 = 12, i = 9, x2 = 24, y0 = 12
A Limitation of this approach
One limitation with this approach is the inability of creating temporary variables whose scope is the comma expression itself.
A work around is to use the do { … } while(0) construct however you then have the problem of the macro is no longer valid in an assignment.
A Few Other Considerations
This sort of thing can be a bit tricky to use. You must always remember that the C Preprocessor is doing text substitution and there is not a great deal of smarts about it.
You should use parenthesis to enforce a specific order of evaluation and to ensure that the substitution text provided when a macro is used will result in the correct order of operations ((i + 3) * 2 and not i + 3 * 2 which are two different statements due to the order of precedence of operators).
Also be aware that using an expression with side effects in a macro that uses a macro argument more than once can result in unintended consequences. For instance:
#define SYMVAR(a,b) ( ((a)=(b)), (b)*2) int xA, xB = 2, xVal; xVal = SYMVAR(xA, xB++); // macro argument has a side effect, post increment
would result in xVal having a value of 6 (xB incremented once, since post increment, and then multiplied by 2), xA having a value of 2 (original value of xB since using post increment) and xB a value of 4 (xB is incremented twice).
You could use one of the above macros in something like:
xx SYMVAR(n1, n2);
which would compile with a warning and then fail during the link. With Visual Studio 2017 I see the following warnings and errors when I add the above statement to the above program. Notice these warnings and errors are all about a missing external function xx and are not compiler errors which you might expect.
Severity Code Description Project File Line Suppression State Warning C4013 'xx' undefined; assuming extern returning int console_scan d:\users\rickc\documents\vs2017repos\console_scan\console_scan\source_macro.c 27 Error LNK2019 unresolved external symbol _xx referenced in function _main console_scan D:\Users\rickc\Documents\vs2017repos\console_scan\console_scan\Source_Macro.obj 1 Error LNK1120 1 unresolved externals console_scan D:\Users\rickc\Documents\vs2017repos\console_scan\Debug\console_scan.exe 1
#define MULTI_STEP(t, x, y) (t = x + y, t * t + 2)(or simply assumed to be declared)getchor similar macro recently that checked whether a buffer contained characters, issued a read operation to get more characters if it did not, and then evaluated to the value of the next character in the buffer (orEOF). This macro needs to be a single expression, as that is howgetch()is used. Just generally, we might implement it as(UpdateBuffer(), Buffer.Next < Buffer.Length ? EOF : Buffer.Data[Buffer.Next++]). So this expression uses the comma operator to evaluate a function then provide a value.SYMVARis expanded to(expr1, expr2, expr3). You might be confusing it with an assigment.x = SYMVAR;would setxto the value ofexpr3, after first evaluating the first two expressions (including side effects), and that sounds like the example you’re looking for.#define SYMVAR (expr1, expr2, expr3)setsSYMVARtoexpr3" it does not. But the preprocessor replaced every occurrenceXby(expr1, expr2, expr3)