A small puzzle. Why does this code work the way it does?

1

0

This was given to me by a friend of mine, it prints out 0 through 9. We have been quite puzzled over this. Does anyone know why this works?

#include <iostream>

using namespace std;

void x(int a)
{
    *(&a-1) += a;
}

int main()
{
    for(int i = 0; i < 10; ++i)
    {
        x(8);
        i = 0;
        cout << i << endl;
    }
}

Ólafur Waage

Posted 2011-12-12T09:31:10.587

Reputation: 121

Question was closed 2011-12-12T23:50:01.067

void(int a) is never printed. That function is just garbage. What is actually being printed is variable i: cout << i << endl; i is started at 0: int i = 0;, then is incremented by 1: ++i;, as long as it is less than 10. The call x(8) does nothing and is unnecessary. – None – 2014-03-10T00:43:27.050

2

CodeGolf.SE is a place to post programming contests with well defined winning conditions. This would, perhaps have been better on Stack Overflow.

– dmckee --- ex-moderator kitten – 2011-12-12T23:52:05.013

1@dmckee I would have thought that "Programming puzzles" in the title would reference stuff like this. Sorry :) – Ólafur Waage – 2011-12-13T10:40:53.053

Imho, this clearly belongs here, not stackoverflow, the closure reflects badly on the moderator. – Jeff Burdges – 2011-12-24T06:54:25.593

Answers

9

First of all, the line *(&a-1) += a; is undefined behavior, so this could do anything depending on compiler version, flags used, alignment of the stars, etc.

That said, the reason why it works in your case is probably because &a-1 happens to point to the return address of x, and incrementing it by 8 causes the i = 0; to be skipped.

hammar

Posted 2011-12-12T09:31:10.587

Reputation: 4 011

3oh my god... that's nasty. – Ólafur Waage – 2011-12-12T12:34:18.347

in other words a buffer overflow without the buffer (or a 1 element buffer) – ratchet freak – 2011-12-12T23:31:17.433

3

The reason this works is because your compiler passes the a parameter on the stack. Before the parameter, the code pushes the return address of the method onto the stack:

004A0008 | 00000008 ; a parameter
004A0004 | 00401200 ; return addr
004A0000 | ...

The &a part of the expression points to the address of the parameter on the stack, in this case I've used 004A0008. The a-1 then subtracts sizeof(int) from that address, giving you 004A0004. The * dereferences that address, so its value can be edited. This means that when you add 8 to the value at 004A0004, it alters the return address. When the method returns, it ends up 8 bytes ahead of where it should have been, skipping the i=0.

Polynomial

Posted 2011-12-12T09:31:10.587

Reputation: 4 082