int a = 5;
a += a++ + a++;
I do remember that this particular code snippet (with a = 5, even) used to be popular as an interview question. I found such questions quite annoying because most interviewers who posed them seemed to believe that whatever output they saw with their compiler version was the correct answer. If you tried explaining that the code has undefined behaviour, the reactions generally ranged from mild disagreement to serious confusion. Most of them neither cared about nor understood 'undefined behaviour' or 'sequence points'.I remember one particular interviewer who, after I explained that this was undefined behaviour and why, listened patiently to me and then explained to me that the correct answer was 17, because the two post-increments leave the variable as 6, so adding 6 twice to the original 5 gives 17.
I am very glad these types of interview questions have become less prevalent these days. They have, right? Right?
If you can convince someone in a position of authority that they’re wrong about something technical without upsetting then you’re probably a good culture fit and someone who can raise the average effectiveness of your team.
I just refuse to do interviews like that any more.
The horrible undefined behavior of signed integer overflow at least can be explained by the fact that multiple CPU architectures handling those differently existed (though the fact that C even 'attracts' its ill-defined signed integers when you're using unsigned ones by returning a signed int when left shifting an uint16_t by an uint16_t for example is not as forgivable imho)
But this here is something that could be completely defined at the language level, there's nothing CPU dependent here, they could have simply stated in the language specification that e.g. the order of execution of statements is from left to right (and/or other rules like post increment happens after the full statement is finished for example, my point is not whether the rule I type here is complete enough or not but that the language designers could have made it completely defined).
Anyway, yes, this one example has an obvious order it should be applied. But still, something like it shouldn't be allowed.
Luckily, I ended up with smug smiles in all those cases after showing them the output from different compilers.
No, if you invoke undefined behavior any result at all is possible.
The problem is that it’s not specified which should be picked, but all pick something.
This doesn't really help portability all that much.
The obvious counterpoint in this particular instance is that there's no good reason not to make such an awful expression a compile time error.
I also personally think that evaluation order should be strictly defined. I'm unclear if the current arrangement ever offers noticable benefits but it is abundantly clear that it makes the language more difficult to reason about.
It seems like something that should trigger a "we should specify this" reaction when adding these operators, and there is at least one reasonable way to define it which is fairly trivial and easily implementable.
But what often happens in practice is that "Bill's Fly-By-Night-C-Compiler-originally-written-in-the-mid-nineties" implemented it in some specific way (probably by accident) and maintains it as a (probably informal) extension. And almost certainly has users who depend on it, and can't migrate for a myriad of reasons. Anyway, it's hard to sell an upgrade when users can't just drop the new compiler in and go.
At the language level, it is undefined-behavior, and any code that relies on it is buggy at the language level, and non-portable.
Defining it would make those compiler non-conforming, instead of just dependent on defining something that is undefined.
Probably the best way forward is to make this an error, instead of defining it in some way. That way you don't get silent changes in behavior.
Undefined behavior allows that to happen at the language level, but good implementations at least try not to break user code without warning.
Modern compilers with things like UBSan and such makes changing the result of undefined behavior much less of an issue. But most UB is also, "No diagnostic required", so users don't even know they have in their code without the modern tools.
UB = run nethack or Emacs:
https://feross.org/gcc-ownage/
We should have kept this behaviour. It would make UB a lot more unpalatable and easy to find.
awk 'BEGIN{a=5; a = a++ + ++a; print a}'
12"When side effects happen is implementation-defined. In other words, it is up to the particular version of awk."
:/ $ awk 'BEGIN{a=5; a = a++ + ++a; print a}'
12
:/ $ which awk
/system/bin/awk
:/ $ awk --version
awk version 20240728I don't do a lot of C anymore, but even when I did, I always would do increments on separate lines, and I would do a +=1, or just a = a + 1. I never noticed a performance degradation, and I also don't think my code was harder to read. In fact I think it was easier since I think the semantics were less ambiguous.
After separating a++ onto its own line, replacing a++ with a+=1 or a=a+1 comes down to personal taste in syntax sugar. I vote for a+=1.
I wouldn't be surprised if someone read `b = expr(a++)` to indicate that `a` is incremented, and then passed into `expr`, especially considering that it is within parentheses. The fact that it does it after passing it in is weird, and not obvious, at least not in my opinion. In my mind, there's no reason not to do what you suggested, or do the increment of `a` on the line before if you want the prefix.
There’s UB, so any answer is possible, isn’t it?
I'm going top-to-bottom through comments, and there was a similar question, so I'll link my answer here: https://news.ycombinator.com/item?id=48140821 (TL;DR: you are right, but there's another perspective on this)
Failing to recognize the dangers would be an instant fail; knowing that something reeks of undefined behaviour, or even potential UB, is enough: you just write out explicitly what you want and skip the mind games.
I <- I++
On the next hour another professor was giving lecture on C++ programming. I asked him the question: what would happen if we compiled i = i++
He went into some deep elaboration on it, but reassumed that only idiot would write like this...If you write:
int i = 0;
i = i++;
and never use the value of i, the declaration and assignment are likely to be optimized out. (The behavior of the assignment is undefined, so this is a valid choice).If you print the value of i, the compiler can still optimize away the computation, but is perhaps less likely to do so.
The solution, of course, is not to write code like that. Decide what you want to do, and write code that does that. "i = i++" will never be the answer to "how do I do this?", and wouldn't be even if the behavior were well defined. If you want i to be 1, write "int i = 1;".
LL and LR parser generates different derivation, and as such it is deterministically non-deterministic, hence UB.
https://www.scribd.com/document/235004757/Test-Your-C-Skills...
No. It was published in late 90s. As per this copy on Archive.org 1997
You should start here:
https://c-faq.com/expr/evalorder2.html
I cannot recommend the C FAQ enough. It is written in an accessible way and contains proper references to textbooks and standards.
Disclosure: I was one of the contributors.
This is how to keep simpletons out of your code base. Every numeric constant is defined in terms of a different lang quiz. Works well in JS as well of course.
const DEFAULT_SELECTION = true + true
const BASE_PRICE = 4 * parseInt(0.0000001)
const BILLING_DAY_OF_MONTH = a++ + ++aIt‘s the standard technical C++ blog post everybody seems to write.
The main, I would say, defining, feature of Java is "no of undefined behavior". Aka "write once, run everywhere".
int a = 5;
int b = a++;
if it gives b==5 in this circumstance (which I would say is the correct value), then it seems that giving 13 for a++ + ++a is a bug in the compiler. I kind of feel like giving 6 as an answer would also be a bug in the compiler since postfix-++ should return the old value and then increment. int a = 5;
int b = a++;
has well defined behavior. The first line initializes a to 5. The second initializes b to 5 and sets a to 6. (The language doesn't specify the order of the two operations of assigning a value to be and incrementing a, but in this case it doesn't matter.)Giving 13 for a++ + ++a is not a bug in the compiler. It's a bug in the code.
The correct answer to "what does a++ + ++a do" is "it gets rejected in code review and replaced with code that expresses the actual intent.
The wise nerd will not allow lines like it in their codebase, in the first place and, having seen one, will refactor it (probably involving more lines or parentheses) to make it more clear and easier to maintain.
The latter approach scales better, in long run.
(this is related to my other comment here https://news.ycombinator.com/item?id=48140821)
#include <stdio.h>
int main() {
int a = 5;
a = a++ + ++a;
printf("%d\n", a);
return 0;
}
x64 msvc v19.50 VS18.2 output: example.c
ASM generation compiler returned: 0
example.c
Execution build compiler returned: 0
Program returned: 0
13
x86-64 gcc 16.1 output: ASM generation compiler returned: 0
Execution build compiler returned: 0
Program returned: 0
12
armv8-a clang 22.1.0 output: <source>:5:10: warning: multiple unsequenced modifications to 'a' [-Wunsequenced]
5 | a = a++ + ++a;
| ^ ~~
1 warning generated.
ASM generation compiler returned: 0
<source>:5:10: warning: multiple unsequenced modifications to 'a' [-Wunsequenced]
5 | a = a++ + ++a;
| ^ ~~
1 warning generated.
Execution build compiler returned: 0
Program returned: 0
12Uh, 85% of them show the wrong result so 85% of them clearly do not support pre and post increment.
int a = 5; a = (++a * a++) + --a; a = ?