目录

C语言: volatile关键字

volatile关键字

来自维基百科的介绍:

在程序设计中,尤其是在C语言、C++、C#和Java语言中,使用volatile关键字声明的变量或对象通常具有与优化、多线程相关的特殊属性。通常,volatile关键字是用来阻止(伪)编译器因误认某段代码无法被代码本身所改变,而造成的过度优化。如在C语言中,volatile关键字可以用来提醒编译器它后面所定义的变量随时有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据。如果没有volatile关键字,则编译器可能优化读取和存储,可能暂时使用寄存器中的值,如果这个变量由别的程序更新了的话,将出现不一致的现象。

在C环境中,volatile关键字的真实定义和适用范围经常被误解。虽然C++、C#和Java都保留了C中的volatile关键字,但在这些编程语言中volatile的用法和语义却大相径庭。

示例:

以下使用的编译器是(x86 msvc v19.14)

比如下面代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#include <stdio.h>


int main() {
  int i=11;
  int a=i;
  printf("i=%d\n",a);
  __asm{
      mov dword ptr [ebp-4], 10h
  }
  int d=i;
  printf("i=%d\n",d);
 return 0;
}

其未进行优化的汇编代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
_DATA   SEGMENT
COMM    ?_OptionsStorage@?1??__local_stdio_printf_options@@9@9:QWORD                                                    ; `__local_stdio_printf_options'::`2'::_OptionsStorage
_DATA   ENDS
_DATA   SEGMENT
$SG7395 DB        'i=%d', 0aH, 00H
        ORG $+2
$SG7396 DB        'i=%d', 0aH, 00H
_DATA   ENDS

_d$ = -12                                         ; size = 4
_a$ = -8                                                ; size = 4
_i$ = -4                                                ; size = 4
_main   PROC
        push    ebp
        mov     ebp, esp
        sub     esp, 12                             ; 0000000cH
        mov     DWORD PTR _i$[ebp], 11                    ; 0000000bH
        mov     eax, DWORD PTR _i$[ebp]
        mov     DWORD PTR _a$[ebp], eax
        mov     ecx, DWORD PTR _a$[ebp]
        push    ecx
        push    OFFSET $SG7395
        call    _printf
        add     esp, 8
        mov     DWORD PTR [ebp-4], 16           ; 00000010H
        mov     edx, DWORD PTR _i$[ebp]
        mov     DWORD PTR _d$[ebp], edx
        mov     eax, DWORD PTR _d$[ebp]
        push    eax
        push    OFFSET $SG7396
        call    _printf
        add     esp, 8
        xor     eax, eax
        mov     esp, ebp
        pop     ebp
        ret     0
_main   ENDP

输出:

1
2
i=11
i=16

开启O2优化的汇编代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
_DATA   SEGMENT
COMM    ?_OptionsStorage@?1??__local_stdio_printf_options@@9@9:QWORD                                                    ; `__local_stdio_printf_options'::`2'::_OptionsStorage
_DATA   ENDS
??_C@_05BKKKKIID@i?$DN?$CFd?6?$AA@ DB 'i=%d', 0aH, 00H  ; `string'

_main   PROC                                      ; COMDAT
        push    11                                  ; 0000000bH
        push    OFFSET ??_C@_05BKKKKIID@i?$DN?$CFd?6?$AA@
        call    _printf
        add     esp, 8
        mov     DWORD PTR [ebp-4], 16           ; 00000010H
        push    11                                  ; 0000000bH
        push    OFFSET ??_C@_05BKKKKIID@i?$DN?$CFd?6?$AA@
        call    _printf
        add     esp, 8
        xor     eax, eax
        ret     0
_main   ENDP

输出:

1
2
i=11
i=11

对代码进行修改,添加volatile

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#include <stdio.h>


int main() {
  volatile int i=11;
  int a=i;
  printf("i=%d\n",a);
  __asm{
      mov dword ptr [ebp-4], 10h
  }
  int d=i;
  printf("i=%d\n",d);
 return 0;
}

开启O2优化的汇编代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
_DATA   SEGMENT
COMM    ?_OptionsStorage@?1??__local_stdio_printf_options@@9@9:QWORD                                                    ; `__local_stdio_printf_options'::`2'::_OptionsStorage
_DATA   ENDS
??_C@_05BKKKKIID@i?$DN?$CFd?6?$AA@ DB 'i=%d', 0aH, 00H  ; `string'

_i$ = -4                                                ; size = 4
_main   PROC                                      ; COMDAT
        push    ebp
        mov     ebp, esp
        push    ecx
        mov     DWORD PTR _i$[ebp], 11                    ; 0000000bH
        mov     eax, DWORD PTR _i$[ebp]
        push    eax
        push    OFFSET ??_C@_05BKKKKIID@i?$DN?$CFd?6?$AA@
        call    _printf
        add     esp, 8
        mov     DWORD PTR [ebp-4], 16           ; 00000010H
        mov     eax, DWORD PTR _i$[ebp]
        push    eax
        push    OFFSET ??_C@_05BKKKKIID@i?$DN?$CFd?6?$AA@
        call    _printf
        add     esp, 8
        xor     eax, eax
        mov     esp, ebp
        pop     ebp
        ret     0
_main   ENDP

输出:

1
2
i=11
i=16