Live data from Hacker News

Int a = 5; a = a++ + ++a; a =? (2011)

gynvael.coldwind.pl

91–100 of 246 posts

Re: Int a = 5; a = a++ + ++a; a =? (2011)

#91
post #44
post #20

Earlier quoted context omitted.

Honestly, having increment in expressions rather than a statement feels like more of a footgun than a benefit. Expressions shouldn't mutate things.

I think the history of this is that these operations were common with assembly programmers, so when C came along, these were included in the language to allow these developers to feel they weren't leaving lots of performance behind. Look at the addressing modes for the PDP-11 in https://en.wikipedia.org/wiki/PDP-11_architecture and you'll see you can write (R0)+ to read the contents of the location pointed to by R0,…

I worked on a more recent custom AI ISA that had that too. Pretty neat; I'm surprised it's not more common. I guess it doesn't matter so much now that memory is so much slower than ALU ops.

Re: Int a = 5; a = a++ + ++a; a =? (2011)

#92
post #20

Earlier quoted context omitted.

Honestly, having increment in expressions rather than a statement feels like more of a footgun than a benefit. Expressions shouldn't mutate things.

Python recently went the other way and added an assignment expression. I actually wish more languages would go further and add statement expressions instead of having to imitate them with IIFEs. C just wouldn't be C without things like a[i++]

If the past few weeks of CVEs indicate anything, it's that C being C maybe isn't a good thing...

Re: Int a = 5; a = a++ + ++a; a =? (2011)

#93
post #43

Earlier quoted context omitted.

> Applying the increment or decrement operators over the same variable more than once on the same line should be a compile-time error That would be nice, but don't forget the more general case of pointers and aliasing: int a = 5; int *pa = &a; printf("%d", (a++ + ++*pa)); The compiler cannot statically catch every possible instance of a statement where a variable is updated more than once.

Well, aliased updates are undefined behavior already.

Not in C, unless at least one of the pointers were marked `restrict`.

Re: Int a = 5; a = a++ + ++a; a =? (2011)

#94
My expectation was none of the four presented. Evaluate left to right, a is five, post-increment, pre-increment, a is seven, 5 + 7 = 12. For right to left I would expect pre-increment, a is six, a is still six, post-increment, a is 7, overwrite with 6 + 6 = 12.

Re: Int a = 5; a = a++ + ++a; a =? (2011)

#95
post #7

What's the reason that C didn't define the order of this? 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…

It's defined. And called "operator precedence", both post/pre-increment have a higher precedence than the single "+". At least according to this: https://en.wikipedia.org/wiki/Operators_in_C_and_C%2B%2B#Exp... I think the main confusion here comes from the fact that "a" is just a value, not a pointer, where it matters when the value/address which the pointer points at is accessed (before of after the increment of the…

Nope. Order of evaluation and operator precedence are completely unrelated. They should have been defined to be the same, but instead order of evaluation was left undefined. So if you write ++a + a++, operator precedence means this will be interpreted as (++a) + (a++), not say ++(a + a)++, but it is up to the compiler whether to execute ++a or a++ first, rather than executing them left to right.

Re: Int a = 5; a = a++ + ++a; a =? (2011)

#96
post #7

What's the reason that C didn't define the order of this? 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…

The short answer is because C was designed to give leeway to really dumb compilers on really diverse hardware. This isn't quite the same case, but it's a good illustration of the effect: on gcc, if you have an expression f(a(), b()), the order that a and b get evaluated is [1] dependent on the architecture and calling-convention of f. If the calling convention wants you to push arguments from right to left, then b is…

gcc used to do this back in the day. Parameter expressions left to right on x86, and right to left on Sparc. I spent a week modifying a bunch of source code, removing expressions with side effects from parameter lists, into my own temporary variables, so that they would all evaluate in the same order.

Re: Int a = 5; a = a++ + ++a; a =? (2011)

#97
post #83
post #74

Earlier quoted context omitted.

> IMO, The only reasonable answer if asked this in an interview is “I would not write code where I have to know the answer to this question” That's half of a reasonable answer. The other half is "but I do know the answer so if I see it when reviewing or working on someone else's code I can flag it or rewrite it, and explain to them why it is bad".

You might still make a mistake, even if you think you know the answer. It's much better to instrument the code to figure it out, or write a short test program.

It's Undefined Behavior. So you can instrument all you want, the answer will still be wrong. You'll capture what your particular compiler does under some particular conditions (opt flags, surrounding code, etc.) but that will not be representative of what can happen in the general case (hint : anything can happen with UB).

Re: Int a = 5; a = a++ + ++a; a =? (2011)

#98
post #25

Earlier quoted context omitted.

I feel we need another category - unspecified behavior. I think everyone would agree the compiler should putout ONE of those answers and that nasal demons would be out of spec. The problem is that it’s not specified which should be picked, but all pick something.

I agree what you say seems reasonable at a glance. But (IIUC) the issue is that for optimization we want the compiler to assume that UB doesn't happen in order to constrain the possible code paths. So if it goes some distance down a possible execution branch and discovers UB it can trim the subtree. At that point "anything can happen" becomes an (approximate) reality. The obvious counterpoint in this particular insta…

As I understand it UB was not really intended to be for optimisation. It was so that C could compile on wildly different architectures that existed at the time.

Today we don't have nearly the variety of architectures, so they in theory C doesn't need nearly as much UB (like more modern languages).

Although there is one modern case where C's "anything goes" attitude has actually helped: CHERI works pretty well with C/C++ even though pointers are double the size they normally are, because doing so many things with pointers is UB (I assume because of segmented memory). CHERI is a slightly awkward target for Rust because Rust makes more assumptions about pointers - specifically that pointers and addresses are the same size.

Re: Int a = 5; a = a++ + ++a; a =? (2011)

#99
post #5

The code in the post seems very similar to the one in my own post from 2010: https://susam.net/sequence-points.html 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 co…

> I am very glad these types of interview questions have become less prevalent these days. They have, right? Right?

Are you referring to the type of interview questions where the question is ill-defined and no one should know the answer, or the type where the question is reasonable and well-defined, but the interviewer doesn't know the answer?

I had a phone screen with Google once where they asked how to determine the length of a stretch of contiguous 1s within an infinite array of 0s. I suggested that, given the starting index i, you can check the index i+2 and then repeatedly square it until you find yourself among the zeroes, after which you can do binary search to find the transition from ones to zeroes.

The interviewer objected that this will grow the candidate end index too quickly, and the correct thing to do is to check index i+1 and then successively double it until you find the zeroes. We moved on.

I passed that phone screen. But I still resent it, because I checked the math later and "successive squaring followed by binary search" and "successive doubling followed by binary search" take exactly the same amount of time.

Re: Int a = 5; a = a++ + ++a; a =? (2011)

#100
post #5

The code in the post seems very similar to the one in my own post from 2010: https://susam.net/sequence-points.html 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 co…

Genuinely curious, so this is undefined behavior and depends on the compiler. I get that. Java, and other languages, can do these same operations but their compilers produce bytecode that runs on a virtual machine (JVM) compiled to machine code just-in-time. Would this same code in Java possibly yield different results based on the platform the JVM was running on because of the platform specific JIT compiler? Maybe that's part of the origin of the phrase "write once, test everywhere".
Post reply on HN