Arjob Mukherjee
Arjob Mukherjee

Reputation: 397

What does #define FOO(x,c) (void)( { c = ( x ) ; }) do?

I was looking at the Linux source and the following line seemed strange. What may be the use of the (void) ( { .... ;} ) construct?

#define GETCH(queue,c) \
        (void)({c=(queue).buf[(queue).tail];INC((queue).tail);})

Here is what I think:

1. (void) suppresses some compiler warnings.

2. {....} is there to group the two statements together, so that it can use used like while(...) GETCH(q,c); correctly.

Is there any other reason? Is there any documentation available?

Source: Linux-0.01 Source - GitHub

Upvotes: 1

Views: 159

Answers (1)

Eric Postpischil
Eric Postpischil

Reputation: 222302

In this macro, the author uses a GCC “statement expression” extension to do two things:

  • Supply two statements, an assignment (c=…) and a use of the INC macro, which I presume performs some increment to its argument.
  • Package those two statements so that, when followed by a semicolon, they form a single statement.

The reason for the latter to allow this macro to be used in places like this:

if (some expression)
    GETCH(queue,c);
else
    some other statement;

For example, suppose the macro had been written using normal braces, like this:

#define GETCH(queue,c) \
    {c=(queue).buf[(queue).tail];INC((queue).tail);}

Then it would supply both statements and group them, so we could use them in simple code like this:

statement X;
GETCH(queue,c);
statement Y;

However, consider the consequences in the if statement. We would have

if (some expression)
    {c=(queue).buf[(queue).tail];INC((queue).tail);};
else
    some other statement;

There is a problem: { … } and ; are two statements. The ; is an empty expression statement. And you cannot have two statements between an if and an else. The compiler will emit a warning when it sees the else after the two statements. But the author of GETCH wanted you to be able to use the macro followed by a semicolon as one statement—they wanted it to “look like” a function call, which you can follow with a semicolon.

So, the author did something else. They used a feature of GCC called a statement expression. It is not a part of the C standard; it is an extension defined by GCC. When using GCC, a compound statement (a statement in braces, { … }) that ends with an expression statement and is enclosed in parentheses (({ … })) acts like an expression whose value is the last expression statement inside the braces. For example ({ if (x) z=x*x; else z=5; 3*z; }) acts like an expression with the value 3*z, in addition to perform the statements inside the braces. Because this is a single expression, you can follow it with a semicolon to form a single statement.

So, if the author had written:

#define GETCH(queue,c) \
    ({c=(queue).buf[(queue).tail];INC((queue).tail);})

then we could write:

if (some expression)
    GETCH(queue,c);
else
    some other statement;

The author also included a cast to (void):

#define GETCH(queue,c) \
    (void)({c=(queue).buf[(queue).tail];INC((queue).tail);})

I expect the purpose of that is to prevent the expression statement from being used as an expression—the author only wanted it to act in the grammar as a single statement and did not want its resulting value used.

That said, I do not see why the author chose to use a GCC extension for this. There is a well known way of doing this in standard C:

#define GETCH(queue,c) \
    do {c=(queue).buf[(queue).tail];INC((queue).tail);} while (0)

The statement inside the do will be executed just once (because it is executed first, then the while expression is checked, and it is false, so no loop occurs), and following the above with a semicolon forms a single statement.

Upvotes: 2

Related Questions