... bug che non è presente in VC++2005.
Il linguaggio C++ mette a disposizione molte keyword che non sono indispensabili ma che rendono il sorgente più leggibile e rendono possibile un controllo più ferreo da parte del compilatore.
Per esempio const serve per rendere read-only una variabile. Il compilatore permette quinidi la sua inizializzazione ma ci restituisce un errore di compilazione qualora provassimo ad assegnare il suo valore successivamente.
Questo è un grosso benefit del compilatore perchè gli errori del compilatore sono nostri amici. Tutto ciò che ci viene rilevato in compilazione, ci evita grossi problemi successivamente ed è questo il motivo per cui keyword come const andrebbero sempre usate per rendere più rigoroso il sorgente.
L'uso di const per esempio fornisce un'immediato guadagno in performance perché il compilatore metterà direttamente il valore della costante nel nostro sorgente invece di eseguire l'accesso in memoria alla variabile, risparmiando così un'istruzione.
Può capitare però di avere un dispositivo hardware, un driver, una zona in shared memory che modifica il valore della variabile dichiarata const, anche se dal nostro sorgente non la modificheremo mai. A questo scopo esiste la keyword volatile. In pratica avvisa il compilatore di non eseguire alcuna ottimizzazione perchè il valore di quella variabile può cambiare al di là di quello che può fare il listato.
Il seguente sorgente, una semplice console Win32 riproduce il bug del compilatore VC++2003.
#include
"stdafx.h"
#include <iostream>
int _tmain(int argc, _TCHAR* argv[])
{
const volatile int iTest = 1;
_asm
{
push 5
pop iTest
}
if(argc == iTest)
std::cout << "equals!";
return 0;
}
Compilato con VC++ 2003 il risultato è questo (per maggiore chiarezza pubblico con il sorgente misto C++ e compilato x86):
const volatile int iTest = 1;
00415D2E C7 45 F8 01 00 00 00 mov dword ptr [iTest],1
_asm
{
push 5
00415D35 6A 05 push 5
pop iTest
00415D37 8F 45 F8 pop dword ptr [iTest]
}
if(argc == iTest)
00415D3A 83 7D 08 01 cmp dword ptr [argc],1 // BUG! Ha sostituito iTest con 1 anche se è volatile
00415D3E 75 12 jne main+42h (415D52h)
std::cout << "equals!";
00415D40 68 C8 00 44 00 push offset string "equals!" (4400C8h)
00415D45 68 D8 92 44 00 push offset std::cout (4492D8h)
00415D4A E8 DA EB FF FF call std::operator<<<std::char_traits<char> > (414929h)
00415D4F 83 C4 08 add esp,8
return 0;
00415D52 33 C0 xor eax,eax
Compilato con VC++ 2005 il risultato è questo:
const volatile int iTest = 1;
0041140E C7 45 F8 01 00 00 00 mov dword ptr [iTest],1
_asm
{
push 5
00411415 6A 05 push 5
pop iTest
00411417 8F 45 F8 pop dword ptr [iTest]
}
if(argc == iTest)
0041141A 8B 45 F8 mov eax,dword ptr [iTest]
0041141D 39 45 08 cmp dword ptr [argc],eax
00411420 75 13 jne wmain+45h (411435h)
std::cout << "equals!";
00411422 68 FC 66 41 00 push offset string "equals!" (4166FCh)
00411427 A1 A8 92 41 00 mov eax,dword ptr [__imp_std::cout (4192A8h)]
0041142C 50 push eax
0041142D E8 FF FC FF FF call std::operator<<<std::char_traits<char> > (411131h)
00411432 83 C4 08 add esp,8
return 0;
00411435 33 C0 xor eax,eax
In sostanza VC++ 2003 ignora la keyword volatile e di conseguenza non è usabile più allo scopo.
L'uso dell'inline assembler simula la presenza di un “qualcosa“ di esterno che modifichi la variabile. Al posto del blocco _asm ci sarebbe potuta essere una shared memory o qualsiasi altra cosa.
Per dovere di cronaca va detto che questa ottimizzazione viene fatta sia in modalità release che in debug (cioè con le ottimizzazioni 'spente'). Ci sono però tante ottimizzazioni più spinte che vegnono effettuate solo in modalità release e per questo è sempre indispensabile fare i test anche sulla versione release.