Diferencia entre lo externo y lo volátil.

Esta pregunta se refiere a la diferencia entre las variables volátiles y externas y también la optimización del comstackdor.

Una variable externa definida en el archivo principal y utilizada en un archivo fuente más, como este:

ExternTest.cpp:

short ExtGlobal; void Fun(); int _tmain(int argc, _TCHAR* argv[]) { ExtGlobal=1000; while (ExtGlobal < 2000) { Fun(); } return 0; } 

Fuente1.cpp:

 extern short ExtGlobal; void Fun() { ExtGlobal++; } 

El ensamblaje generado para esto en el vs2012 como abajo:

Ensamblaje ExternTest.cpp para acceder a la variable externa

 ExtGlobal=1000; 013913EE mov eax,3E8h 013913F3 mov word ptr ds:[01398130h],ax while (ExtGlobal < 2000) 013913F9 movsx eax,word ptr ds:[1398130h] 01391400 cmp eax,7D0h 01391405 jge wmain+3Eh (0139140Eh) 

Ensamblaje de Source.cpp para modificar la variable externa

 ExtGlobal++; 0139145E mov ax,word ptr ds:[01398130h] 01391464 add ax,1 01391468 mov word ptr ds:[01398130h],ax 

Desde el conjunto anterior, cada acceso a la variable “ExtGlobal” en el bucle while lee el valor de la dirección correspondiente. Si agrego volátil a la variable externa, se generó el mismo código de ensamblaje. El uso volátil en dos hilos diferentes y el uso de variables externas en dos funciones diferentes son iguales.

Preguntar por lo extern y lo volatile es como preguntar por los cacahuetes y los gorilas. Están completamente sin relación.

extern se usa simplemente para decirle al comstackdor, “Oye, no esperes encontrar la definición de este símbolo en este archivo C. Deja que el enlazador lo arregle al final”.

esencialmente, volatile le dice al comstackdor: “Nunca confíe en el valor de esta variable. Incluso si acaba de almacenar un valor de un registro en esa ubicación de memoria, no reutilice el valor en el registro; asegúrese de volver a leerlo desde memoria.”

Si desea ver que volatile hace que se genere un código diferente, escriba una serie de lecturas / escrituras a partir de la variable.

Por ejemplo, comstackndo este código en cygwin, con gcc -O1 -c ,

 int i; void foo() { i = 4; i += 2; i -= 1; } 

Genera el siguiente assembly:

 _foo proc near mov dword ptr ds:_i, 5 retn _foo endp 

Tenga en cuenta que el comstackdor sabía cuál sería el resultado, por lo que simplemente siguió adelante y lo optimizó.

Ahora, agregar volatile a int i genera lo siguiente:

 public _foo _foo proc near mov dword ptr ds:_i, 4 mov eax, dword ptr ds:_i add eax, 2 mov dword ptr ds:_i, eax mov eax, dword ptr ds:_i sub eax, 1 mov dword ptr ds:_i, eax retn _foo endp 

El comstackdor nunca confía en el valor de i , y siempre lo vuelve a cargar desde la memoria.