Why does C provide both the comma operator and the semicolon to separate statements

coperatorsprogramming-languages

Both the comma operator and the semicolon can be used to separate statements. Let's consider this simple code:

#include <stdio.h>

void do_something(int*i) {(*i)++;}

int main() {
    int i;
    scanf("%d", &i),
    i++,
    do_something((i++, &i)),
    printf("i is now %d\n", i);
    if(i == 5) printf("MAGIC NUMBER\n");
    return 0;
}

I've been experimenting when can I use the comma instead of the semicolon. It turns out that the semicolon is mandatory after int i and before return and if; and, of course, after the closing brace.

Why can't we have only one statement separating facility? Why weren't the comma and the semicolon be blended? Their use seems to greatly overlap, only that in some contexts (like before keywords) we can only use the semicolon, while in some other contexts (like in the while loop condition or in a function argument) we can only use the comma.

Best Answer

You are generally right that the comma operator and the semicolon are very similar and both serve as a sequence point. In some languages, there is only a single operator that serves both purposes.

However, the C syntax is statement-oriented. Statements and Expressions play very different roles in its grammar. So while a semicolon is used to separate statements, the comma operator can be used to separate expressions. This has the advantage of making the syntax a bit more robust. E.g. this is clearly a syntax error:

foo(f();
g());

whereas foo((f(), g())) is valid, but usually bad style.

In expression-oriented languages, there are no or fewer statement-level syntax constructs. In these languages, there may not be a comma operator. E.g. in Rust, it is possible to embed statements in an expression, like

foo({ f(); g() });

Here, a comma operator is no longer necessary. Note that the GCC compiler offers a similar syntax extension for C and C++. In standard C++11, you can get similar syntax through an immediately invoked lambda:

foo([]{ f(); return g(); }());