f1(int x)
{
if(x = 1) return 1;
return (x * f1(--x));
}
vs
int f2(int x)
{
if(x==1)return 1;
return (x * f2(x-1));
}
Il bit interno del codice f1
viene compilato su ( gcc -S ...
):
.L2:
subl $1, -4(%rbp)
movl -4(%rbp), %eax
movl %eax, %edi
call f1
imull -4(%rbp), %eax
Mentre f2
si compila in
.L2:
movl -4(%rbp), %eax
subl $1, %eax
movl %eax, %edi
call f2
imull -4(%rbp), %eax
Puoi vedere che le operazioni di subl
e movl
sono in un ordine diverso tra i due. In particolare, f1 sta sottraendo prima prima che il valore venga spostato perché venga utilizzato nel calcolo.
f1 ha un effetto collaterale di modificare il valore di x
quando richiama --x
. Questo effetto collaterale è un ordine in modo che accada al valore prima che il multiplo venga valutato con lo stesso valore .
In f1, hai:
First invocation
x = 5; x * f1(--x) --> x = 4 ; x * f1(4)
Second invocation
x = 4; x * f1(--x) --> x = 3 ; x * f1(3)
Third invocation
x = 3; x * f1(--x) --> x = 2 ; x * f1(2)
Fourth invocation
x = 2; x * f1(--x) --> x = 1 ; x * f1(1)
Ora hai popolato questi valori:
x = 1 ; x * f1(1) ; 1 * 1 --> return 1
x = 2 ; x * f1(2) ; 2 * 1 --> return 2
x = 3 ; x * f1(3) ; 3 * 2 --> return 6
x = 4 ; x * f1(4) ; 4 * 6 --> 24
Questo perché ti trovi in un'area indefinita del compilatore quando fai x * --x
. Per consentire determinate ottimizzazioni, alcune cose devono essere indefinite. Questo è uno di questi: il compilatore può elaborarlo come vuole.
Non utilizzare --x
o x--
a meno che non desideri la modifica dell'assegnazione.
so I was thinking that x will be always evaluated before --x. please tell me why I am wrong here? The compiler should not therefore try to optimize in these types of situations
Da 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.
Non c'è nessun punto di sequenza nell'espressione x * --x
, quindi non c'è alcun punto in cui il compilatore c garantirà che la valutazione avvenga in un certo ordine.
L' associatività di un operatore deve occuparsi di come viene analizzata un'espressione.
Considera 4 * 3 + 2
. L'associatività e la precedenza di * e + guarntees che viene analizzata come (4 * 3) + 2
.
Gli operatori matematici sono lasciati associati, mentre l'assegnazione è giusta associativa. Mentre 4 + 3 + 2 + 1
è analizzato come (((4 + 3) + 2) + 1)
- non importa troppo con questo. Le assegnazioni sono giuste associate. a = b = c = 4
diventa (a = (b = (c = 4)))
. Osserva da che parte è il gruppo delle parentesi.
Questo non ha alcun rapporto con la questione su quale lato di un termine matematico viene valutato per primo nel contesto di x * --x
.