4
#include <stdio.h>

int main()
{
    int sum = 0, result = (({for (int i = 0; i < 5; i++) sum += i;}), sum);
    
    printf("result = %d\n", result);

    return 0;
}

displays

result = 10

I'm using whatever the default version of C is in gcc. Is it specific to gcc? I'm aware (a, b) returns b but I didn't expect to get it to work when a was a {} block. Can anyone explain why it works and why the block has to be bracketed to get it to work?

EDIT: This is a contrived simplified example of a #define I was trying out. Some programmer dude's comment (thanks) clears it up. Case closed.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
NoComprende
  • 731
  • 4
  • 14
  • 13
    [Statement-expressions](https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html) is a GCC-specific extension, it's not part of standard C. – Some programmer dude Aug 09 '23 at 10:29
  • More generally, [the comma expression](https://en.cppreference.com/w/c/language/operator_other#Comma_operator) evaluates the left-hand side of the expression, throws away the result, and then evaluates the right-hand side. In that specific order. The result of the comma expression is result of the the right-hand side only. – Some programmer dude Aug 09 '23 at 10:30
  • 1
    You can easily put the code inside a function and print the value of `result()` without any fancy compiler extensions. – BoP Aug 09 '23 at 10:42
  • `int sum = 0, result = (({for (int i =0; i < 5; i++) sum +=i;}), sum);`?!?! Where's the eye bleach? Hopefully this is just a contrived example to illustrate your question and not real code written by some misguided person. That code takes readability and maintainability, burns them, flushes the ashes down the toilet, follows the flow to the sewage treatment plant, and nukes the entire sewage treatment plant from orbit just to be sure both readability and maintainability are dead and can never be found again. – Andrew Henle Aug 09 '23 at 10:50

1 Answers1

5

I'm using whatever the default version of c is in gcc.

Currently that's -std=gnu17, equivalent to "lax ISO C17 with GNU extensions". It is not a conforming mode and not recommended for beginners.

Is it specific to gcc?

Yes, or rather it is specific to the GNU C dialect, which is at least partially supported by clang as well. As mentioned in comments, this is a non-standard extension known as "statement expressions" or "statements in expressions". It allows multiple expressions/lines and optionally returns a value like a function-like macro.

I'm aware (a, b) returns b but I didn't expect to get it to work when a was a {} block

The comma operator has a special evaluation rule guaranteeing that the sub-expression a is fully evaluated before b. Normally this isn't the case with C operators. This means that you can assume that everything at a is executed before b and you may even safely include code using b.

However, a list of variables to be initialized does not come with such a sequencing guarantee. int sum = 0, result=sum; does not guarantee any particular order or sequencing - code such as this is deeply problematic and possibly undefined behavior.


Best practices are to follow standard C whenever possible, to write code as readable as possible and to avoid poorly-defined behavior. Complex "one-liner" code is very bad practice. So this would have been much better written as:

int sum = 0;
for (int i=0; i < 5; i++) 
{
  sum+=i;
}
int result = sum;

Or if you prefer, the 100% equivalent:

int result = 10;
Lundin
  • 195,001
  • 40
  • 254
  • 396
  • @EricPostpischil Ah yeah, for some reason I assumed that these were globals. Will fix, thanks. – Lundin Aug 09 '23 at 11:19
  • *However, a list of variables to be initialized does not come with such a sequencing guarantee. int sum = 0, result=sum; does not guarantee any particular order or sequencing - code such as this is deeply problematic and possibly undefined behavior.* This is deeply disturbing. Can you substantiate with references to the C Standard or possibly a WG14 document? – chqrlie Aug 09 '23 at 11:53
  • @chqrlie I think the statement is incorrect. – Ian Abbott Aug 09 '23 at 11:57
  • 2
    @IanAbbott: I asked a separate question: https://stackoverflow.com/questions/76867659/does-the-definition-int-a-0-b-a-c-a-have-defined-behavior-in-c specific to the C language, this behavior seems fully defined in C++. – chqrlie Aug 09 '23 at 12:14
  • The evaluation of `0` in `int sum = 0, result=sum;` is a *full expression*, so there is a sequence point between evaluation of `0` and evaluation of the next full expression to be evaluated, which is `sum` in the init-declarator `result=sum`. The value of `sum` is not indeterminate because it was initialized explicitly. – Ian Abbott Aug 09 '23 at 12:14
  • @chqrlie I might remember this wrong, but C only guarantees a sequence point after a so-called "full declarator". In the expression `int sum = 0, result=sum;`, the (full) declarators are `sum` and `result`. And so the sequence points should be like this: `sum = 0, result=sum;`... which is senseless for plain objects/scalar declarators but not for array/aggregate declarators. – Lundin Aug 09 '23 at 12:39
  • @Lundin: the question was indeed addressed in [this](https://stackoverflow.com/a/39431962/4593267) answer. Your interpretation is somewhat extreme but not unfounded. – chqrlie Aug 09 '23 at 12:48
  • @chqrlie Looking into it now, there appears to be several shortcomings in the ISO 9899. First of all the infomative Annex C makes various claims about sequence points after full declarators and after initializers, which the actual normative text does not make at all. And then apparently the text defining full declarators was changed in C17 and I cannot find why they changed it. – Lundin Aug 09 '23 at 12:52
  • @Lundin Annex C was also changed in C17 to remove "the end of a full declarator" from the list of sequence points, except that a "full declarator for a variably modified type" is now also a "full expression", and there is a sequence point between the evaluation of a full expression and the next full expression to be evaluated. (Annex C is not normative, but see C17 6.8p4 & 6.8p5 for the normative text.) – Ian Abbott Aug 09 '23 at 14:09
  • @IanAbbott Err, okay... clearly an improvement... or...? :) Some of these changes just seem random and not taking the big picture in account. I wish they would provide more of a rationale in the actual standard. Instead we end up with yet another "murky waters" section of the language which one best stay clear of when writing code. – Lundin Aug 09 '23 at 14:18