Si compilas con GCC, puedes pasar el argumento "-S" para que genere el ensamblador en vez de el binario. Además, pasarle -O0 para que no optimice el código (y así tener el código tal como tú lo generaste).
Por ejemplo, este es el ASM desglosado del programa que probé (suerte que lo recuperé de la papelera de reciclaje) (está en C++, pero ninguna parte importante):
#include <iostream>
using namespace std;
int main(){
int n = 1;
cout << n++ + ++n * n++;
}
/*
movl $1, n ; int n = 1
movl n, %edx
leal 1(%edx), %eax
movl %eax, n ; n++ (first, edx = old value)
addl $1, n ; ++n
movl n, %eax
leal 1(%eax), %ecx
movl %ecx, n ; n++ (second, eax = old value)
imull n, %eax
addl %edx, %eax
*/
No sé si controlas ensamblador, y no es parte del tema, pero como breve resumen:
movl mueve el valor del primer operando al segundo.
leal es más complejo. pero aquí, en resumen, cuando pone 1(operando), lo que hace es sumarle 1 al operando y guardarlo en el segundo operando
imull multiplica y almacena el resultado en el segundo operando
addl suma y almacena elr esultado en el segundo operando
Separé cada "operador" por un salto de línea. (Inicialmente no ponía "n", ponía -12(%esp), que es donde se almacenan las variables locales. Pero lo cambié a n por legibilidad.
Y bueno, el ensamblador no miente. Primero, mueve un 1 a n.
Luego, hace el primer post-incremento, y almacena el valor antiguo (que es el que se suma realmente), en edx (registro, que serían como las variables de ASM).
Siguiente, hace el pre-incremento. Sin ir más lejos, añade 1. Trivial.
Luego, segundo post-incremento. Incrementa n, y almacena el valor antiguo en eax.
Y ahora las otras operaciones. Empezamos ya con el problema que había. Hace
imull n, %eax. eax es el valor del post-incremento, todo correcto, pero, como vimos, n lo ha incrementado ya las 3 veces, así que va a multiplicar por ese valor.
Y finalmente, la suma, trivial también.
God bless you, y recuerda que si tú juegas con el lenguaje, el lenguaje jugará contigo :X