Come vengono valutate le espressioni in Turbo C ++?

-1

Stavo leggendo di casts in c ++ e mi sono confuso su come sono effettivamente valutate le espressioni. Considera il seguente codice in cui var è di tipo int e dopo l'espressione var = (var*10)/10 la memoria di var viene overflow:

#include<iostream.h>

void main()
{
  int var = 25000; // signerd int varies from -32768 to 32767
  var = (var*10)/10; // seems like (var*10) is stored in the memory allocated 'var'  
  cout << "Incorrect value of var is:" << var << endl;

  var = (long(var)*10)/10;
  cout << "correct value of var is:" << var << endl;
}

Il valore errato mostrato è -1214 e il corretto è, come previsto, 25000. Sono molto confuso su come viene effettivamente valutata l'espressione var = (var*10)/10; . Il compilatore valuta l'espressione passo dopo passo in modo che il risultato della prima operazione [(var * 10)] eseguita sul valore numerico di var sia memorizzato nella memoria allocata, quindi il valore memorizzato in tale spazio di memoria viene utilizzato per ulteriori operazioni da eseguire.

Il mio jerk al ginocchio era che il compilatore calcolava direttamente il valore (var * 10), quindi divide questo valore con 10, ma questo non sembra essere il caso.

Quindi la domanda è,

  • How are expressions, in general and for my specific example, evaluated in C++?

P.S: So che sto usando un dialetto obsoleto del linguaggio C ++ e del compilatore C ++.

    
posta user106313 18.02.2016 - 18:41
fonte

3 risposte

1

Sì e no. Almeno in un caso tipico, il compilatore non memorizzerà i risultati intermedi nella posizione di memoria originale. In genere, tuttavia, esegue le operazioni su un registro delle stesse dimensioni e ogni risultato intermedio verrà riscritto in quel registro. Quindi, per il tuo caso, il compilatore potrebbe generare codice come questo:

mov ax, var ; load the value from 'var' into register ax
mov bx, 10  ; load 10 into register dx
imul bx     ;  multiply ax * bx. 32-bit result:
            ; 16 least significant bits in AX
            ; 16 most significant bits in DX
xor dx, dx  ; zero dx, in this case losing some significant digits
idiv bx     ; divide ax by bx. 32-bit result:
            ; 16-bit dividend in ax
            ; 16-bit remainder in dx
            ; if dividend won't fit in 16 bits, trigger a "divide by 0" exception
mov var, ax ; store final result back into var's location in memory

Ciascuno di questi registri (ax, bx, dx, ecc.) ha una dimensione di 16 bit.

Quando esegui la divisione su long anziché int , fa grosso modo la stessa cosa, ma l'intermedio verrà eseguito con precisione a 32-bit anziché solo 16 bit di precisione.

Con un compilatore che sapeva come generare istruzioni a 32 bit sarebbe praticamente identico, tranne che sostituirà ax con eax , bx con ebx e dx con edx . Un compilatore a 16 bit non saprà comunque come generare queste istruzioni, quindi normalmente è necessario utilizzare alcune routine di libreria per ottenere la precisione a 32 bit.

    
risposta data 18.02.2016 - 20:19
fonte
3

How are expressions, in general and for my specific example, evaluated in C++?

La valutazione delle espressioni è un po 'complessa da descrivere in dettaglio in una risposta, quindi mi concentrerò sui tuoi esempi.

var = (var*10)/10;

In questo caso, possono accadere due cose. Poiché il valore può essere calcolato in fase di compilazione, il compilatore può eseguire il calcolo nello stesso modo in cui si verificherebbe al runtime e memorizzare direttamente il risultato. Tieni presente che C ++ non specifica come deve accadere qualcosa, si concentra sui risultati. In altre parole, lo standard dice fondamentalmente "un'espressione deve valutare un risultato usando una certa semantica", ma un compilatore è libero di precomputare, riordinare le operazioni, ecc. finché il risultato è lo stesso .

L'altra opzione è che il compilatore non fa nulla e il calcolo si verifica in fase di esecuzione, e questo valore andrà in overflow e produrrà risultati indesiderati come altri hanno notato.

var = (long(var)*10)/10;

Funziona allo stesso modo, tranne che i valori intermedi sono memorizzati in un long invece di un int che è generalmente più grande (ma non obbligatorio).

Si noti che in entrambi gli esempi qualsiasi calcolo in fase di esecuzione utilizzerà valori intermedi: solo l'operatore = memorizzerà il valore in var . Qualsiasi altro valore può essere memorizzato in variabili temporanee create dal compilatore, registri o nel cervello dei demoni nasali - lo standard consente al compilatore di fare quello che vuole finché il risultato finale è lo stesso .

Ulteriori letture

Raccomando di leggere su constexpr . Anche se il tuo vecchio compilatore non lo supporta, qualsiasi discussione su questa nuova parola chiave approfondirà gli argomenti trattati in questa risposta.

risposta data 18.02.2016 - 19:02
fonte
2

Does the compiler evaluates the expression step by step such that the result of first operation[ (var*10) ] performed on the numeric value of var is stored in it's allocated memory then the value stored in that memory space is used for further operations to be performed.

Sì.

My knee jerk was that the compiler directly computes the value (var*10) then divides this value with 10 but this doesn't seem to be the case.

Non vedo la distinzione.

Sia calcolato in fase di compilazione (grazie, ottimizzatore!) o in fase di esecuzione, la semantica del tipo var deve ancora deve essere rispettata. Se non può contenere valori superiori a 32768, allora non può contenere valori superiori a 32768 e, indipendentemente dalla quantità di magia che il compilatore esegue per eseguire il calcolo il più rapidamente possibile, tale fatto non può e non cambierà .

Attenzione, l'overflow dei caratteri interi non è definito (ovvero può succedere qualsiasi cosa ) quindi suppongo che in teoria potrebbe vedere il risultato previsto qui, ma non terrei il mio respiro. ;)

    
risposta data 18.02.2016 - 18:50
fonte

Leggi altre domande sui tag