← Back to context

Comment by mFixman

4 hours ago

I used to slay with this in code golfing competitions from TopCoder, where you had to implement a function to solve a particular problem, thanks to C pointer maths and the gcc generally putting function arguments in order in the stack.

Turns out, these two are equivalent in practice (but UB in the C++ standard):

    double solve(double a, double b, double c, double d) {
      return a + b + c + d;
    }

    double solve(double a ...) {
      return a + 1[&a] + 2[&a] + 3[&a];
    }

> Turns out, these two are equivalent in practice

Not in the x86-64 SysV ABI they aren’t. The arguments will be passed in registers (yes, even the variadic ones), so how your compiler will interpret 1[&a] is anybody’s guess. (For me, x86_64-unknown-linux-gnu-g++ -O2 yields, essentially, return a+a+a+a; which is certainly an interpretation. I’m also getting strange results from i686-unknown-linux-gnu-g++ -O2, but my x87 assembly is rusty enough that I don’t really get what’s going on there.)

  • > return a+a+a+a; which is certainly an interpretation.

    Zero is the only valid index of &a, so I presume the compiler just assumes that all the indexes in 1[&a] + 2[&a] etc must be zero. Even though they're in this case compile-time constants – the optimizer could check but why bother given that it's UB anyway. I presume modern C/C++ compilers have some flag to diagnose indexing that's known to be OOB at compile time.

K&R syntax is -1 char, if you are in C:

    double solve(double a,double b,double c,double d){return a+b+c+d;}
    double solve(double a...){return a+1[&a]+2[&a]+3[&a];}
    double solve(a,b,c,d)double a,c,b,d;{return a+b+c+d;}

    double solve(double a[]) {
      return 0[a] + 1[a] + 2[a] + 3[a];
    }

    solve((double[]){1, 2, 3, 4});

The cast in the invocation can be macro-ed away. And the best thing is, the actual stack layout and data movement/shuffling is pretty much identical to the approach with <stdargs.h>, and with no UB or compiler intrinsics.