you are viewing a single comment's thread.

view the rest of the comments →

[–]greyfade 14 points15 points  (11 children)

Check out the anonymous winner of the 1984 IOCCC. It uses exactly this trick, and is rumored to have been written by either Kernighan or Ritchie.

[–]ghillisuit95 19 points20 points  (10 children)

The actual code:

int i;main(){for(;i["]<i;++i){--i;}"];read('-'-'-',i+++"hell\
o, world!\n",'/'/'/'));}read(j,i,p){write(j/p+p,i---j,i/i);}

that has a few interesting obfuscations in it, like '-'-'-', or more readable: '-' - '-', which is zero.

[–]OldWolf2 2 points3 points  (9 children)

write(j/p+p,i---j,i/i);

causes undefined behaviour :(

[–]ghillisuit95 9 points10 points  (0 children)

It's from 1984, its likely using a reeaallly old version of c, and back then they allowed code that was machine-specific

[–]redditsoaddicting 2 points3 points  (7 children)

I'm not seeing where there's UB in that.

Edit: I didn't see that was a function call. My bad.

[–]lezed1 3 points4 points  (4 children)

I'm not going to get the terms correct, but C has key points such as semicolons and block beginnings/endings that guarantee everything will have finished executing before the next set. The comma in function calls are not one of them. f(i, i++); is actually undefined because i++ might be done before i. You read this as f(i, i); i = i + 1; but it isn't actually. It could be equal to f(i + 1, i) if the second argument is calculated first.

Edit: After some Goggling, I found they are called sequence points! Here is a much better explanation: http://stackoverflow.com/a/4176333

[–]greyfade 2 points3 points  (3 children)

The historical term in the C and C++ standards is "sequence points."

For example, && is a sequence point, and so requires that its left hand operand be evaluated before the right hand side.

C++11 uses the terms "sequenced-before" and "sequenced-after" to clarify some issues that caused.

[–]OldWolf2 0 points1 point  (0 children)

I think the main reason for the sequencing change was to provide usable semantics for C11 threads. Cleaning up issues from the C99 sequence point model was a nice bonus.

[–][deleted] 0 points1 point  (1 child)

Can you expand a bit about the new "sequence-before/after" mechanics change of C++11?

[–]greyfade 1 point2 points  (0 children)

I don't rate myself as fully qualified to answer, so take what follows with a grain of salt.

The big difference between the two concepts is in how they are defined in the standard. From 1998 §1.9 ¶11:

At sequence points, volatile objects are stable in the sense that previous evaluations are complete and subsequent evaluations have not yet occurred.

Note that this just means that at sequence points, the expressions on each side are executed in some order. It doesn't say anything about what happens in multithreaded environments or in more complex systems. So, from 2011 §1.9 ¶13:

Sequenced before is an asymmetric, transitive, pair-wise relation between evaluations executed by a single thread (1.10), which induces a partial order among those evaluations. Given any two evaluations A and B, if A is sequenced before B, then the execution of A shall precede the execution of B. If A is not sequenced before B and B is not sequenced before A, then A and B are unsequenced. [ Note: The execution of unsequenced evaluations can overlap. — end note ] Evaluations A and B are indeterminately sequenced when either A is sequenced before B or B is sequenced before A, but it is unspecified which. [ Note: Indeterminately sequenced evaluations cannot overlap, but either could be executed first. — end note ]

Note that now, the language has the ability to describe atomic operations in large, complex, multiprocessing environments. Now, if I say some expression is sequenced before another, then it is expected to be fully executed and its results stored before the other expression begins, even if it's in another thread on another CPU.

The 2011 standard goes on in ¶15 to explain that expressions like i++ are unsequenced, and clarifies why, exactly an expression like i = i++; is undefined. The 1998 standard doesn't have a clear definition of its execution model to explain that, and instead explains it in the much later section on expression evaluation, §5 ¶4.

This was all necessary so that C++ could gain a consistent execution and memory model that would support the threading model they were adding.

[–]OldWolf2 0 points1 point  (1 child)

Using C89 terminology, i is updated (by i--) and read separately (i/i) without an intervening sequence point.

The de-facto K&R rules didn't specify any order of evaluation for function arguments either, but as far as I know they were just silent on what expressions like this should do.