编译器编译过程探究

编译器编译过程探究

该度过 2019-05-07 11:15:49  246  收藏 1
展开
摘要: 完整的语言处理系统包括预处理器、编译器、汇编器、连接-编辑器四个组成部分。一个典型的编译过程为:预处理器将源程序中的宏展开为原始语句加入到源程序中,编译器则产生汇编代码,汇编代码交由汇编器产生可重定位机器代码,然后与一些库程序连接在一起形成绝对机器代码,即可在计算机上执行的代码。本文以GCC为工具,对简单的C,C++程序进行编译,观察其各个部分的输出内容,探究语言处理系统所做的完整工作。

关键字:预处理 编译 汇编 链接 问题探究

1 引言

        一个 C 或者 C++ 语言程序在编译成为可执行目标程序的过程中需要经历预处理、编译、汇编、链接四个阶段。如下图所示:

源程序
目标汇编程序
可重定位机器代码
绝对机器代码
预处理器
编译器
汇编器
连接器
可执行目标程序
本文将采用如下几段 C 或 C++ 代码,对编译的过程及结果进行分析,探究语言系统所做的完整工作。

// test.cc
#include<iostream>
using namespace std;
int main(){
    int i,n,f;
    cin>>n;
    i=2;
    f=1;
    while(i<=n){
        f=f*i;
        i=i+1;
    }
    cout<<f<<endl;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//test2.c
#include <stdio.h>
#include "mymath.h"// 自定义头文件
int main(){
    int a = 2;
    int b = 3;
    int sum = add(a, b); 
    printf("a=%d, b=%d, a+b=%d\n", a, b, sum);
}
1
2
3
4
5
6
7
8
9
// mymath.h(储存在文件夹 math 中)
#ifndef MYMATH_H
#define MYMATH_H
int add(int a, int b){return a+b;}
int sub(int a, int b){return a-b;}
#endif
1
2
3
4
5
6
2 预处理阶段

        预处理器产生编译器的输入。通过预处理,可以将储存在不同文件中的程序模块集成为一个完成的源程序,另外还可以将宏展开为原始语句加入到头文件中。其功能概括为:宏处理、文件包含、语言扩充、'理性’预处理器。
在命令行中执行语句:
Gcc -E test.cc -o test.ii 和 Gcc -E -I./math test2.c -o test2.i
此处,-E 要求 Gcc 只进行预处理而不进行后面的三个阶段,-I 指出头文件所在的目录, -o 指目标文件,.i 文件为预处理后的 C 源程序。预处理后的代码如下:

//test.ii  (由于原文件过长,此处截取了部分代码)
# 1 "test.cc"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "test.cc"
namespace std
{
  typedef unsigned int size_t;
  typedef int ptrdiff_t;


  typedef decltype(nullptr) nullptr_t;

}
namespace std
{
  inline namespace __cxx11 __attribute__((__abi_tag__ ("cxx11"))) { }
}
namespace __gnu_cxx
{
  inline namespace __cxx11 __attribute__((__abi_tag__ ("cxx11"))) { }
}
namespace std
{


extern "C" {

 __attribute__((__cdecl__)) __attribute__((__nothrow__)) int iswalnum (wint_t);
 __attribute__((__cdecl__)) __attribute__((__nothrow__)) int iswalpha (wint_t);
 __attribute__((__cdecl__)) __attribute__((__nothrow__)) int iswascii (wint_t);
 __attribute__((__cdecl__)) __attribute__((__nothrow__)) int iswcntrl (wint_t);
 __attribute__((__cdecl__)) __attribute__((__nothrow__)) int iswctype (wint_t, wctype_t);
 __attribute__((__cdecl__)) __attribute__((__nothrow__)) int iswdigit (wint_t);
 __attribute__((__cdecl__)) __attribute__((__nothrow__)) int iswgraph (wint_t);
 __attribute__((__cdecl__)) __attribute__((__nothrow__)) int iswlower (wint_t);
 __attribute__((__cdecl__)) __attribute__((__nothrow__)) int iswprint (wint_t);
 __attribute__((__cdecl__)) __attribute__((__nothrow__)) int iswpunct (wint_t);
 __attribute__((__cdecl__)) __attribute__((__nothrow__)) int iswspace (wint_t);
 __attribute__((__cdecl__)) __attribute__((__nothrow__)) int iswupper (wint_t);
 __attribute__((__cdecl__)) __attribute__((__nothrow__)) int iswxdigit (wint_t);

__attribute__((__deprecated__))

 __attribute__((__cdecl__)) __attribute__((__nothrow__)) int is_wctype (wint_t, wctype_t);


__attribute__((__cdecl__)) __attribute__((__nothrow__)) int iswblank (wint_t);

 __attribute__((__cdecl__)) __attribute__((__nothrow__)) wint_t towlower (wint_t);
 __attribute__((__cdecl__)) __attribute__((__nothrow__)) wint_t towupper (wint_t);
 
 ...
 
 # 2 "test.cc"
using namespace std;
int main(){
 int i,n,f;
 cin>>n;
 i=2;
 f=1;
 while(i<=n){
  f=f*i;
  i=i+1;
 }
 cout<<f<<endl;
}

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
//test2.i (由于原文件过长,此处截取了部分代码)
# 1 "test4.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "test4.c"
typedef struct _iobuf
{
  char *_ptr;
  int _cnt;
  char *_base;
  int _flag;
  int _file;
  int _charbuf;
  int _bufsiz;
  char *_tmpfname;
} FILE;

...

# 2 "test4.c" 2
# 1 "./math/mymath.h" 1

# 4 "./math/mymath.h"
int add(int a, int b){return a+b;}
int sub(int a, int b){return a-b;}
# 3 "test4.c" 2
int main(){
    int a = 2;
    int b = 3;
    int sum = add(a, b);
    printf("a=%d, b=%d, a+b=%d\n", a, b, sum);
}
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
可以看出头文件及宏定义的代码已经加入到源程序中,所以 .i 文件的体积远大于原文件的体积。

3 编译阶段

        编译器是分阶段执行的,如图为编译器的一个典型的阶段划分:

源程序
词法分析器
语法分析器
语义分析器
中间代码生成器
代码优化器
代码生成器
目标程序
首先是对代码进行语法检查,此处运行命令行代码 gcc -I./math -fsyntax-only test4.c,其中 test4.c 是 test2.c 中 add(a,b) 后去掉分号的文件。 命令行运行后给出如下信息:

test4.c: In function 'main':
test4.c:7:5: error: expected ',' or ';' before 'printf'
     printf("a=%d, b=%d, a+b=%d\n", a, b, sum);
     ^~~~~~
1
2
3
4
说明出现了语法错误,重新加上 ’ ; ',正常运行。

        另外,在编译阶段还可以对代码进行优化,此处依次运行命令行代码 :gcc -o test2.c,gcc -O3 test2.c,gcc -os test2.c. 其中,-o为默认执行,不进行任何优化;-O3 为优化等次3,-os 为优化代码大小,实际相当于优化等次2.5。将生成.exe文件分别为:a.exe、3.exe、s.exe。不过由于windows下没有直接显示进程运行时间的命令,通过查阅资料,写出如下的批处理,可以输出运行的时间,其代码如下:

//time.bat
@echo off
set /a StartMS=%time:~3,1%*60000 + %time:~4,1%*6000 + %time:~6,1%*1000 + %time:~7,1%*100 + %time:~9,1%*10 + %time:~10,1%
%1 %2 %3 %4 %5 %6
set /a EndMS =%time:~3,1%*60000 + %time:~4,1%*6000 + %time:~6,1%*1000 + %time:~7,1%*100 + %time:~9,1%*10 + %time:~10,1%
set /a realtime = %EndMS%-%StartMS%
echo %realtime%ms
1
2
3
4
5
6
7
运行命令:.\time.bat D:\Gcc\test2\a、.\time.bat D:\Gcc\test2\3、.\time.bat D:\Gcc\test2\s即可查看对应的运行时间,运行结果如下:

PS D:\Gcc\test2> .\time.bat D:\Gcc\test2\a
a=2, b=3, a+b=5
6ms
PS D:\Gcc\test2> .\time.bat D:\Gcc\test2\3
a=2, b=3, a+b=5
4ms
PS D:\Gcc\test2> .\time.bat D:\Gcc\test2\s
a=2, b=3, a+b=5
3ms
1
2
3
4
5
6
7
8
9
可以看出,未经优化时,运行消耗的时间更多,-os执行效率最高。

编译阶段的最终结果是生成机器目标代码文件,执行命令:Gcc -S test.cc -o test.S、Gcc -S test4.c -o test4.S,生成汇编代码如下:

test.S的内容:

    #test.S
    .file    "test.cc"
    .section .rdata,"dr"
__ZStL19piecewise_construct:
    .space 1
.lcomm __ZStL8__ioinit,1,1
    .def    ___main;    .scl    2;    .type    32;    .endef
    .text
    .globl    _main
    .def    _main;    .scl    2;    .type    32;    .endef
_main:
LFB1445:
    .cfi_startproc
    leal    4(%esp), %ecx
    .cfi_def_cfa 1, 0
    andl    $-16, %esp
    pushl    -4(%ecx)
    pushl    %ebp
    .cfi_escape 0x10,0x5,0x2,0x75,0
    movl    %esp, %ebp
    pushl    %ecx
    .cfi_escape 0xf,0x3,0x75,0x7c,0x6
    subl    $36, %esp
    call    ___main
    leal    -20(%ebp), %eax
    movl    %eax, (%esp)
    movl    $__ZSt3cin, %ecx
    call    __ZNSirsERi
    subl    $4, %esp
    movl    $2, -12(%ebp)
    movl    $1, -16(%ebp)
L3:
    movl    -20(%ebp), %eax
    cmpl    %eax, -12(%ebp)
    jg    L2
    movl    -16(%ebp), %eax
    imull    -12(%ebp), %eax
    movl    %eax, -16(%ebp)
    addl    $1, -12(%ebp)
    jmp    L3
L2:
    movl    -16(%ebp), %eax
    movl    %eax, (%esp)
    movl    $__ZSt4cout, %ecx
    call    __ZNSolsEi
    subl    $4, %esp
    movl    $__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, (%esp)
    movl    %eax, %ecx
    call    __ZNSolsEPFRSoS_E
    subl    $4, %esp
    movl    $0, %eax
    movl    -4(%ebp), %ecx
    .cfi_def_cfa 1, 0
    leave
    .cfi_restore 5
    leal    -4(%ecx), %esp
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
LFE1445:
    .def    ___tcf_0;    .scl    3;    .type    32;    .endef
___tcf_0:
LFB1875:
    .cfi_startproc
    pushl    %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
    subl    $8, %esp
    movl    $__ZStL8__ioinit, %ecx
    call    __ZNSt8ios_base4InitD1Ev
    leave
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
LFE1875:
    .def    __Z41__static_initialization_and_destruction_0ii;    .scl    3;    .type    32;    .endef
__Z41__static_initialization_and_destruction_0ii:
LFB1874:
    .cfi_startproc
    pushl    %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
    subl    $24, %esp
    cmpl    $1, 8(%ebp)
    jne    L8
    cmpl    $65535, 12(%ebp)
    jne    L8
    movl    $__ZStL8__ioinit, %ecx
    call    __ZNSt8ios_base4InitC1Ev
    movl    $___tcf_0, (%esp)
    call    _atexit
L8:
    nop
    leave
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
LFE1874:
    .def    __GLOBAL__sub_I_main;    .scl    3;    .type    32;    .endef
__GLOBAL__sub_I_main:
LFB1876:
    .cfi_startproc
    pushl    %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
    subl    $24, %esp
    movl    $65535, 4(%esp)
    movl    $1, (%esp)
    call    __Z41__static_initialization_and_destruction_0ii
    leave
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
LFE1876:
    .section    .ctors,"w"
    .align 4
    .long    __GLOBAL__sub_I_main
    .ident    "GCC: (MinGW.org GCC-6.3.0-1) 6.3.0"
    .def    __ZNSirsERi;    .scl    2;    .type    32;    .endef
    .def    __ZNSolsEi;    .scl    2;    .type    32;    .endef
    .def    __ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_;    .scl    2;    .type    32;    .endef
    .def    __ZNSolsEPFRSoS_E;    .scl    2;    .type    32;    .endef
    .def    __ZNSt8ios_base4InitD1Ev;    .scl    2;    .type    32;    .endef
    .def    __ZNSt8ios_base4InitC1Ev;    .scl    2;    .type    32;    .endef
    .def    _atexit;    .scl    2;    .type    32;    .endef
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
以下为test4.S的内容:

    //test4.S
    .file    "test4.c"
    .text
    .globl    _add
    .def    _add;    .scl    2;    .type    32;    .endef
_add:
LFB10:
    .cfi_startproc
    pushl    %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
    movl    8(%ebp), %edx
    movl    12(%ebp), %eax
    addl    %edx, %eax
    popl    %ebp
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
LFE10:
    .globl    _sub
    .def    _sub;    .scl    2;    .type    32;    .endef
_sub:
LFB11:
    .cfi_startproc
    pushl    %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
    movl    8(%ebp), %eax
    subl    12(%ebp), %eax
    popl    %ebp
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
LFE11:
    .def    ___main;    .scl    2;    .type    32;    .endef
    .section .rdata,"dr"
LC0:
    .ascii "a=%d, b=%d, a+b=%d\12\0"
    .text
    .globl    _main
    .def    _main;    .scl    2;    .type    32;    .endef
_main:
LFB12:
    .cfi_startproc
    pushl    %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
    andl    $-16, %esp
    subl    $32, %esp
    call    ___main
    movl    $2, 28(%esp)
    movl    $3, 24(%esp)
    movl    24(%esp), %eax
    movl    %eax, 4(%esp)
    movl    28(%esp), %eax
    movl    %eax, (%esp)
    call    _add
    movl    %eax, 20(%esp)
    movl    20(%esp), %eax
    movl    %eax, 12(%esp)
    movl    24(%esp), %eax
    movl    %eax, 8(%esp)
    movl    28(%esp), %eax
    movl    %eax, 4(%esp)
    movl    $LC0, (%esp)
    call    _printf
    movl    $0, %eax
    leave
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
LFE12:
    .ident    "GCC: (MinGW.org GCC-6.3.0-1) 6.3.0"
    .def    _printf;    .scl    2;    .type    32;    .endef

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
编译器将高级语言源程序转换为汇编语言程序,体现在文件上为将.i文件转换为.S文件。

4 汇编阶段

        通过编译器产生的汇编代码需要交由汇编器进行进一步的处理,生成可重定位的机器代码,体现在文件上为将.S文件转换为.o文件。此时生成的.o文件为二进制文件,文本编辑器将无法打开。为查看生成文件的内容,此处采用GUN的objdump进行反汇编。具体过程为:

Gcc test.S -o test.o //执行完汇编阶段即停止
objdump -d test.o //对文件 test.o 进行反汇编
1
2
执行结果如下:

//反汇编后的代码:

test.o:     file format pe-i386

Disassembly of section .text:

00000000 <_main>:
   0:   8d 4c 24 04             lea    0x4(%esp),%ecx
   4:   83 e4 f0                and    $0xfffffff0,%esp
   7:   ff 71 fc                pushl  -0x4(%ecx)
   a:   55                      push   %ebp
   b:   89 e5                   mov    %esp,%ebp
   d:   51                      push   %ecx
   e:   83 ec 24                sub    $0x24,%esp
  11:   e8 00 00 00 00          call   16 <_main+0x16>
  16:   8d 45 ec                lea    -0x14(%ebp),%eax
  19:   89 04 24                mov    %eax,(%esp)
  1c:   b9 00 00 00 00          mov    $0x0,%ecx
  21:   e8 00 00 00 00          call   26 <_main+0x26>
  26:   83 ec 04                sub    $0x4,%esp
  29:   c7 45 f4 02 00 00 00    movl   $0x2,-0xc(%ebp)
  30:   c7 45 f0 01 00 00 00    movl   $0x1,-0x10(%ebp)
  37:   8b 45 ec                mov    -0x14(%ebp),%eax
  3a:   39 45 f4                cmp    %eax,-0xc(%ebp)
  3d:   7f 10                   jg     4f <_main+0x4f>
  3f:   8b 45 f0                mov    -0x10(%ebp),%eax
  42:   0f af 45 f4             imul   -0xc(%ebp),%eax
  46:   89 45 f0                mov    %eax,-0x10(%ebp)
  49:   83 45 f4 01             addl   $0x1,-0xc(%ebp)
  4d:   eb e8                   jmp    37 <_main+0x37>
  4f:   8b 45 f0                mov    -0x10(%ebp),%eax
  52:   89 04 24                mov    %eax,(%esp)
  55:   b9 00 00 00 00          mov    $0x0,%ecx
  5a:   e8 00 00 00 00          call   5f <_main+0x5f>
  5f:   83 ec 04                sub    $0x4,%esp
  62:   c7 04 24 00 00 00 00    movl   $0x0,(%esp)
  69:   89 c1                   mov    %eax,%ecx
  6b:   e8 00 00 00 00          call   70 <_main+0x70>
  70:   83 ec 04                sub    $0x4,%esp
  73:   b8 00 00 00 00          mov    $0x0,%eax
  78:   8b 4d fc                mov    -0x4(%ebp),%ecx
  7b:   c9                      leave
  7c:   8d 61 fc                lea    -0x4(%ecx),%esp
  7f:   c3                      ret

00000080 <___tcf_0>:
  80:   55                      push   %ebp
  81:   89 e5                   mov    %esp,%ebp
  83:   83 ec 08                sub    $0x8,%esp
  86:   b9 00 00 00 00          mov    $0x0,%ecx
  8b:   e8 00 00 00 00          call   90 <___tcf_0+0x10>
  90:   c9                      leave
  91:   c3                      ret

00000092 <__Z41__static_initialization_and_destruction_0ii>:
  92:   55                      push   %ebp
  93:   89 e5                   mov    %esp,%ebp
  95:   83 ec 18                sub    $0x18,%esp
  98:   83 7d 08 01             cmpl   $0x1,0x8(%ebp)
  9c:   75 1f                   jne    bd <__Z41__static_initialization_and_destruction_0ii+0x2b>
  9e:   81 7d 0c ff ff 00 00    cmpl   $0xffff,0xc(%ebp)
  a5:   75 16                   jne    bd <__Z41__static_initialization_and_destruction_0ii+0x2b>
  a7:   b9 00 00 00 00          mov    $0x0,%ecx
  ac:   e8 00 00 00 00          call   b1 <__Z41__static_initialization_and_destruction_0ii+0x1f>
  b1:   c7 04 24 80 00 00 00    movl   $0x80,(%esp)
  b8:   e8 00 00 00 00          call   bd <__Z41__static_initialization_and_destruction_0ii+0x2b>
  bd:   90                      nop
  be:   c9                      leave
  bf:   c3                      ret

000000c0 <__GLOBAL__sub_I_main>:
  c0:   55                      push   %ebp
  c1:   89 e5                   mov    %esp,%ebp
  c3:   83 ec 18                sub    $0x18,%esp
  c6:   c7 44 24 04 ff ff 00    movl   $0xffff,0x4(%esp)
  cd:   00
  ce:   c7 04 24 01 00 00 00    movl   $0x1,(%esp)
  d5:   e8 b8 ff ff ff          call   92 <__Z41__static_initialization_and_destruction_0ii>
  da:   c9                      leave
  db:   c3                      ret
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
5 装载-连接阶段

        装配器完成程序的装入和连接编辑两项功能。装入过程包括读入可重定位机器代码,修改可重定位地址,并将修改后的指令和数据放到内存中的合适位置。连接编辑器将多个重定位机器代码的文件组装成为一个程序。

        经过汇编之后的.o文件依然是不可执行的,只有经过连接阶段,将程序所引用的外部文件关联起来,生成.exe文件才是可执行的程序。

        连接的方式有静态链接与动态链接,动态链接的代码是存放在动态链接库或者某个共享对象的目标文件中,不会将库的内容拷贝到可执行程序中,所以生成的程序的体积较小;而静态链接库则将需要的代码从相应的静态链接库中拷贝到可执行程序中。此处采用了动态链接库的方式。命令代码为:

gcc -fPIC -shared test.cc -o libtest.so  //链接库libtest.so
gcc test.o -o test  //生成可执行程序test.exe
.\test  //执行test.exe  
1
2
3
运行结果如下:

D:\Gcc\test>.\test
5
120
1
2
3
libtest.so 反汇编后的代码(由于文件过长截取部分内容,详见附件):

D:\Gcc\test>objdump -d libtest.so

libtest.so:     file format pei-i386


Disassembly of section .text:

61d41000 <.text>:
61d41000:       53                      push   %ebx
61d41001:       83 ec 18                sub    $0x18,%esp
61d41004:       8b 15 04 50 d4 61       mov    0x61d45004,%edx
61d4100a:       85 d2                   test   %edx,%edx
61d4100c:       74 39                   je     61d41047 <.text+0x47>

        .......

61d41ba0 <__DTOR_LIST__>:
61d41ba0:       ff                      (bad)
61d41ba1:       ff                      (bad)
61d41ba2:       ff                      (bad)
61d41ba3:       ff 00                   incl   (%eax)
61d41ba5:       00 00                   add    %al,(%eax)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
        

参考

肖文鹏. Linux汇编语言开发指南(EB/OL). http://www.ibm.com/developerworks/cn/linux/l-assembly/index.html, 2003-07-03
作者不详. GCC优化选项的各种含义以及潜藏风险(EB/OL). http://www.360doc.com/content/16/0802/17/478627_580294703.shtml, 2016-08-02
作者不详. bat时间的运算与提取(EB/OL). https://www.cnblogs.com/--3q/p/5723277.html, 2016-07-31
gcc manual(EB/OL). http://gcc.gnu.org/onlinedocs/gcc-4.2.2/gcc/
gcc中文文档
————————————————
版权声明:本文为CSDN博主「该度过」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_39999637/article/details/82857251

展开阅读全文
©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客
应支付0元
点击重新获取
扫码支付

支付成功即可阅读