So you think you know C?

5 points by dmk23 ↗ HN
Solve this multiple choice puzzle and explain your answer

  int i = 5;
  i = ++i + ++i;
  return i;

  a) 12
  b) 13
  c) 14
  d) Undefined

17 comments

[ 3.6 ms ] story [ 51.7 ms ] thread
b). ++i increments i before the equality operation and is evaluated inline.

PS why, are you having hiring difficulties or something?

Not having any difficulties, just ran into this problem and thought it would be fun to share with HN.

By the way your answer is wrong.

How come? b) seems correct in my book.
B may seem like it is the "correct" answer, in so much as you are getting that out of your test programs. But because this code is undefined the functionality is compiler dependent and cannot be counted on. It is liable to change at a moment's notice and shouldn't make it into production code.

The C spec doesn't handle this case. The only correct answer is undefined.

Which version of C? The answers vary. For 1st ed. K&R, you're going to get implementation-specific behaviour, which might very well vary whether or not optimization is enabled.
"Implementation-specific behavior [..] which might very well vary"

In other words, the very definition of undefined behavior; You cannot reason about how the program will behave. This topic has already been covered fairly well on SO: http://stackoverflow.com/questions/949433/could-anyone-expla...

Not quite. C and C++ standards discriminate between undefined- and implementation-defined behavior. In the latter case, implementaitons can differ, but each must choose (and document) some well-defined way to handle a case. A trivial example is "sizeof(int)". The standard does not say much about what it produces (IIRC, it must be an int at least as large as sizeof(char)), so it can return 1, 2, 4, or even 17 (well, maybe not in C11. Newer standards tend to put more restrictions on behavior)

A nontrivial example is the value of "-64 >> 3". More info at http://stackoverflow.com/questions/2397984/undefined-unspeci...

I think the post you reply to refers to the fact that early K&R C predates any standard that introduced the term 'undefined behavior'. At that time, the standard was just 'whatever that tool Kernighan and Ritchie wrote does'.

Even at that point, there was the PDP-11 compiler and the compiler for the Perkins-Elmer implementation, not to mention the (straight) C compiler (/usr/bin/cc) vs the Portable C Compiler, both of which were available on V7.

The maxim then (and now) being: Don't Write Silly Code.

The answer is D, D a thousand times D.

Firstly, from Wikipedia

A sequence point defines any point in a computer program's execution at which it is guaranteed that all side effects of previous evaluations will have been performed, and no side effects from subsequent evaluations have yet been performed.

-----

Secondly from Annex C of the C99 Standard: Sequence Points

1. The following are sequence points described in 5.1.2.4

- The call to a function, after the arguments have been evaluated (6.5.2.2).

- The end of the first operand of the following operators: logical AND && (6.5.13); logical OR || (6.5.14); conditional ? (6.5.17).

- The end of a full declarator: declarators (6.7.5);

- The end of a full expression: an initializer (6.7.8); the expression in an expression statement (6.8.3); the controlling expression for a while or do statement (6.8.5); each of the expressions fro a for statement (6.8.5.3); the expression in a return statement (6.8.6.4).

- Immediately before a library function returns (7.1.4).

- After the actions associated with each formatted input/output function conversion specified (7.19.6, 7.24.2).

- Immediately before and immediately after each call to a comparison function and also between any call to a comparison function and any movement of the object passed as arguments to the call (7.20.5).

-----

Now onto this statement

i = ++i + ++i;

lacking in sequence points between the double assignments is undefined and has been undefined for the life of the language. It is as undefined as all of it's ugly brethren and should be left to die.

Why does this question keep popping up?

> The answer is D, D a thousand times D.

Do you mean option D or D, the Language?

Option D, the language, while interesting, doesn't really have any bearing on the question.
I get that the answer is "undefined". But I decided to figure out why at least on my compiler I get "14".

    $ cat t.c ; gcc ./t.c

    #include<stdio.h>
    int main() {
        printf("%d\n", a());
    }

    int a() {
        int i = 5;
        i = ++i + ++i;
        return i;
    }
    $ ./a.out
    14 # ??

    $ gcc t.c -c -o t.bin
    $ gdb t.bin
    ...
    (gdb) disassemble a
    Dump of assembler code for function a:
    0x0000000000000030 <a+0>:	push   %rbp
    0x0000000000000031 <a+1>:	mov    %rsp,%rbp
    0x0000000000000034 <a+4>:	movl   $0x5,-0xc(%rbp)
    0x000000000000003b <a+11>:	mov    -0xc(%rbp),%eax
    0x000000000000003e <a+14>:	add    $0x1,%eax            # add 1 and store it in i => 6
    0x0000000000000041 <a+17>:	mov    %eax,-0xc(%rbp)
    0x0000000000000044 <a+20>:	mov    -0xc(%rbp),%eax
    0x0000000000000047 <a+23>:	add    $0x1,%eax            # add 1 and store it in i => 7
    0x000000000000004a <a+26>:	mov    %eax,-0xc(%rbp)       
    0x000000000000004d <a+29>:	mov    -0xc(%rbp),%eax
    0x0000000000000050 <a+32>:	mov    -0xc(%rbp),%ecx      # move current value of i (7) into %ecx and %eax (both registers are now 7) 
    0x0000000000000053 <a+35>:	add    %ecx,%eax            # add them together => 14
    0x0000000000000055 <a+37>:	mov    %eax,-0xc(%rbp)
...
(comment deleted)
(comment deleted)
(comment deleted)
(comment deleted)