本文是与《C++编程基础及应用》(暂定名)第12章 - 编译及构建配套的操作指南,用于演示在Windows命令行环境下使用mingw编译器手工编译并运行程序的过程。

版权声明

本文可以在互联网上自由转载,但必须:注明出处(作者:海洋饼干叔叔)并包含指向本页面的链接。

本文不可以以纸质出版为目的进行改编、摘抄。

1. 准备mingw编译环境

  在Windows菜单中找到命令提示符(通过输入cmd进行查找),然后点击进入。

1

  在命令提示符中执行命令 gcc -v, 期望如果如下图所示。

image-20221031081723865

  如果系统报告“未知的程序”’gcc’ 不是内部或外部命令,也不是可运行的程序或批处理文件“,则说明mingw编译环境尚未得到正确的安装及配置。请按照下述链接中的第一部分进行安装及配置。

http://codelearn.club/2022/09/vsccpp/

3. 准备示例代码目录及文件

  在D:\Demo目录里准备如下的三个源代码文件,如果读者的计算机上没有D盘,那就使用C:\Demo目录。

image-20221031082351535

  其中,compute.h内容如下:

1
2
3
4
5
6
7
8
#ifndef _COMPUTE_H
#define _COMPUTE_H

#define PI 3.1415926 //comment
#define SQUARE(x) x*x
float circleArea(const float r);

#endif

  文件compute.c内容如下:

1
2
3
4
5
6
7
#include "compute.h"
#include "compute.h"

float circleArea(const float r){
float t = PI * SQUARE(r); //comment
return t;
}

  文件area.c内容如下:

1
2
3
4
5
6
7
8
#include <stdio.h>
#include "compute.h"

int main(){
float r = 4.1f;
float a = circleArea(r); //comment
printf("Area of the circle = %f.\n",a);
}

3. 使用命令行编译并运行

  如下图所示,在Windows命令提示符(终端)中依次执行如表1所列的命令,完成上述3个源代码文件的编译,并成功执行了名为area.exe的可执行文件。

image-20221031083042346

表1 在终端里使用gcc编译示例程序并运行
命令 d:
说明 切换当前盘符为D盘,如果读者使用的是C:\Demo目录,则不要执行该命令。
命令 cd \Demo
说明 将当前目录切换为当前盘符下的\Demo目录,示例的3个源代码文件在该目录里。cd是操作系统终端命令,意为改变目录(change directory)。
命令 dir
说明 终端命令,显示当前目录下的所有文件及子目录。执行结果显示当前目录下有area.c、compute.h及compute.c三个文件。
命令 gcc area.c compute.c –o area
说明 使用gcc编译器编译area.c及compute.c两个C语言程序文件,生成名为area的可执行目标文件。请注意,头文件compute.h不被视为一个单独的编译单元,所以未在命令中列出。上述命令行中的-o选项意为指定输出文件的名称。该文件执行完成后,再次执行dir,可见当前目录下多了一个名为area.exe的文件,其扩展名exe说明该文件是一个可执行文件(executable file)。
命令 area.exe
说明 载入并执行当前目录下名为area.exe的可执行目标文件。在执行该命令后,操作系统会将该程序载入内存,然后将执行点转移至该程序的起始处。随后的执行结果可见,area成功计算并打印了半径为4.1的圆的面积。

  上述示例程序的编译/构建过程可用下图表示:

proc

4. 预处理

  执行命令

1
gcc -E compute.c -o compute.i

  或者

1
cpp compute.c compute.i

  都可以对源代码文件compute.c进行预处理,对应的输出文件为compute.i。如下图所示:

  结果文件compute.i系文本文件,可以使用任意文字编辑软件,比如记事本, 打开。当然,也可以使用Visual Studio Code或者Qt Creator等IDE环境来查看。

  在作者的计算机上,compute.i的内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 0 "compute.c"
# 0 "<built-in>"
# 0 "<command-line>"
# 1 "compute.c"
# 1 "compute.h" 1





float circleArea(const float r);
# 2 "compute.c" 2


float circleArea(const float r){
float t = 3.1415926 * r*r;
return t;
}

  编译预处理的具体工作原理请参阅教材第12章。

5. 编译

  使用下述命令可以将compute.i进行编译,得到包含汇编语言指令的结果文件compute.s。

image-20221031084826462

  同理,compute.s也是文本文件,可用记事本,Visual Studio Code,Qt Creator等工具打开查看。在作者的计算机上,compute.s的内容如下,其包含的是x86的汇编语言指令序列。

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
	.file	"compute.c"
.text
.globl circleArea
.def circleArea; .scl 2; .type 32; .endef
.seh_proc circleArea
circleArea:
pushq %rbp
.seh_pushreg %rbp
movq %rsp, %rbp
.seh_setframe %rbp, 0
subq $16, %rsp
.seh_stackalloc 16
.seh_endprologue
movss %xmm0, 16(%rbp)
pxor %xmm1, %xmm1
cvtss2sd 16(%rbp), %xmm1
movsd .LC0(%rip), %xmm0
mulsd %xmm0, %xmm1
pxor %xmm0, %xmm0
cvtss2sd 16(%rbp), %xmm0
mulsd %xmm1, %xmm0
cvtsd2ss %xmm0, %xmm0
movss %xmm0, -4(%rbp)
movss -4(%rbp), %xmm0
addq $16, %rsp
popq %rbp
ret
.seh_endproc
.section .rdata,"dr"
.align 8
.LC0:
.long 1293080650
.long 1074340347
.ident "GCC: (GNU) 12.2.0"

6. 汇编

  在Windows命令行中执行下述命令之一,可以将compute.s汇编成可重定位目标文件(relocatable object file)compute.o,其为包含机器语言指定的二进制文件。

1
2
as compute.s -o compute.o
gcc -c compute.s -o compute.o

image-20221031085253304

  结果文件compute.o是二进制文件,直接查看其内容只会得到一堆0和1。在Windows命令行里,可以通过下述命令对其进行反汇编,查看其中的汇编语言指令:

image-20221031085454693

7. 链接

  对于编译单元compute.c,经过预处理-编译-汇编之后我们得到可重定位目标文件compute.o。

  我们对编译单元area.c进行类似处理,得到可重定位目录文件area.o。

image-20221031085834165

  这样,我们就有了两个可重定位目标文件,分别是area.o及compute.o,其中,area.o包含了对compute.o内部的函数的调用。

  通过下述命令,我们将area.o及compute.o链接为一个整体,得到可执行文件area.exe,然后再次执行area.exe。

image-20221031090008118