浅显易懂的GCC使用教程

前言:
   2018-12-17天气暖,属于冬日里出太阳。最近在学习使用gvim,想着抛弃对IDE的依赖同时也是想了解编译的过程,但除了学习gvim繁多的指令外还得先学习使用gcc编译程序。这篇文章将会用浅显易懂的方式记录下gcc的基本使用,同时也不忘扩展探究。

  • 什么是GCC,它能干什么?
  • GCC、gcc、g++三者有何关系?
  • 开始开发前该做什么准备?
  • gcc常用指令讲解?

1 什么是gcc,它能干什么?

  GCC(GNU Compiler Collection)即GNU编译器套件,属于一种编程语言编译器,其原名为GCC(GNU C Compiler)即GNU c语言编译器,虽然缩写一样但是功能上区别很大。GCC的初衷是为GNU操作系统专门编写的一款编译器,原本的GNU是专用于编译C代码,现如今已扩展为可以编译C、C++、Java、Objective-C等多种编程语言的编译器集合了。这篇文章主要介绍gcc或g++的使用。

2 GCC、gcc、g++三者有何关系?

  gcc(GUN C Compiler)是GCC中的c编译器,而g++(GUN C++ Compiler)是GCC中的c++编译器。
  gcc和g++两者都可以编译c和cpp文件,但存在差异。gcc在编译cpp时语法按照c来编译但默认不能链接到c++的库(gcc默认链接c库,g++默认链接c++库)。g++编译.c和.cpp文件都统一按cpp的语法规则来编译。所以一般编译c用gcc,编译c++用g++。后文有时间会继续深入探讨区别。

3 开始开发前该做什么准备?

3.1 软件安装教程

  直接去官网上下载MinGW的包管理器。
  下载好后安装包管理器并运行它,可以看到如下界面:

浅显易懂的GCC使用教程

前言:
   2018-12-17天气暖,属于冬日里出太阳。最近在学习使用gvim,想着抛弃对IDE的依赖同时也是想了解编译的过程,但除了学习gvim繁多的指令外还得先学习使用gcc编译程序。这篇文章将会用浅显易懂的方式记录下gcc的基本使用,同时也不忘扩展探究。

  • 什么是GCC,它能干什么?
  • GCC、gcc、g++三者有何关系?
  • 开始开发前该做什么准备?
  • gcc常用指令讲解?

1 什么是gcc,它能干什么?

  GCC(GNU Compiler Collection)即GNU编译器套件,属于一种编程语言编译器,其原名为GCC(GNU C Compiler)即GNU c语言编译器,虽然缩写一样但是功能上区别很大。GCC的初衷是为GNU操作系统专门编写的一款编译器,原本的GNU是专用于编译C代码,现如今已扩展为可以编译C、C++、Java、Objective-C等多种编程语言的编译器集合了。这篇文章主要介绍gcc或g++的使用。

2 GCC、gcc、g++三者有何关系?

  gcc(GUN C Compiler)是GCC中的c编译器,而g++(GUN C++ Compiler)是GCC中的c++编译器。
  gcc和g++两者都可以编译c和cpp文件,但存在差异。gcc在编译cpp时语法按照c来编译但默认不能链接到c++的库(gcc默认链接c库,g++默认链接c++库)。g++编译.c和.cpp文件都统一按cpp的语法规则来编译。所以一般编译c用gcc,编译c++用g++。后文有时间会继续深入探讨区别。

3 开始开发前该做什么准备?

3.1 软件安装教程

  直接去官网上下载MinGW的包管理器。
  下载好后安装包管理器并运行它,可以看到如下界面:

Package Manager.png

  左侧选择“Basic Setup”选项,右侧勾选(点击“Mark for Installation”)需要安装的组件“mingw32-gcc-g++”。假设你也需要使用MinGW编译Fortran、Object-C、Ada语言的话也可以勾上相应的选项。选择好后点击左上角的Installation菜单中的Apply changes选项即开始在线下载安装相关组件。
  安装好后最好配置下环境变量,把MinGw的bin目录加入到环境变量的path中,这样dos在任意目录下都可以调用bin目录下的可执行文件,其他一些软件也能找到gcc的位置。cmd中输入gcc -v查看版本号。

Version.png

4 gcc常用指令讲解?

  用gcc在Windows上编译*.c文件并非直接生成*.exe文件(Linux上为*.out),中间还经历了预处理、编译和汇编几个过程,好在gcc提供了生成中间文件的指令,虽然平时开发的时候很少关注编译过程中生成的这些*.i和*.s文件,但对其有一定的了解总归是好的。

Process.png

4.1 gcc指令

  使用gcc指令编译*.c文件可以生成*exe可执行文件。下面我们用一段简单的c代码来讲解,Demo.c内源码如下:

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

int main(void)
{
printf("nihao\n");

return 0;
}

  打开cmd,需要注意的是cmd默认路径一般是c盘,而Demo.c文件可能并不在cmd的默认路径下,我们需要使用dos指令cd到Demo.c所在目录下才能对文件进行操作。

Order gcc.png

  对Demo.c文件执行gcc指令后会在该目录下生成可执行文件a.exe,由于没有制定exe的文件名所以默认为a.exe。

a exe.png

  当然也不一定要cd进Demo.c的目录,使用gcc指令时给出Demo.c的绝对路径也可以成功编译,命令如下:

cmd
1
gcc E:\WorkSpace\Test_gcc\Demo.c

4.2 【指令】 -o

  指令-o(小写)用来指定生成的文件名。

Order o.png

结果如下:

Demo exe.png

  生成的文件不一定要在.c所在目录,可以给出路径指定:

cmd
1
gcc Demo.c -o ..\Demo.exe

  该指令指定将exe生成到上一级目录。
  生成exe后我们试者执行一下看看结果。

Run.png

4.3 【指令】 -E(预处理(Preprocessing))

  指令 -E(大写)将执行预处理操作也即生成*.i文件,gcc编译器将对#开头的指令进行解析。我们修改Demo.c中的代码为如下:

c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <stdlib.h>
#include "Test.h"

int main()
{
int a = N; //宏常量
int b = 2;
int c = 0;

c = a + b;
printf("%d\n", c);

CODE //宏替换代码段

DoNothing();

system("pause");
return 0;
}

  在Demo.c同目录下编辑Test.h文件,源码如下:

c
1
2
3
4
5
6
7
8
#define N 1

#define CODE if(c > 2) \
{ \
printf("c > 2\n"); \
}

void DoNothing(void); //函数声明(该函数未被调用)

  在Demo.c同目录下编辑Test.c文件,源码如下:

c
1
2
3
4
5
6
7
8
#include"Test.h"

void DoNothing(void)
{
;

return ;
}

  从源码可知Demo.c中在预处理阶段需要把调用的头文件包含进来,替换宏常量和宏代码段。我们执行指令。

cmd
1
gcc -E Demo.c -o Demo.i

  并看看Demo.i文件中的代码,Demo.i文件内容如下。

Order E.png

  -E 指令使gcc执行预处理,预处理时对#类指令进行处理(包含头文件、替换宏常量和宏代码段等操作),也就不难理解为何Demo.i文件会很大了(38KB),整个工程最后被揉合成一个大文件。预处理把注释都去掉了,所以反编译回来的代码是没有注释的!因为注释早就被去掉了。另外可以一提的是使用指令gcc -E Demo.c不指定输出的文件名时内容将会直接输出到Dos框中,而不会产生文件。
  我们知道c语言出现语法错误时编译器将会报错,但检查语法错误是在预处理、编译、汇编、链接、生成可执行文件中的哪个阶段执行的呢?为了方便我们编辑Demo2.c代码,有意使源码代码由于错误测试预处理阶段是否会报语法错误。

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

int main(void)
{
aabbccdd //此处有语法错误

return 0;
}

  对Demo2.c执行预处理操作gcc -E Demo2.c

Preprocessing Error.png

  由此可知预处理阶段不检查语法错误。

4.4 【指令】 -S(编译(Compiling))

  执行-S(大写)指令将*.i文件中源码转化为汇编代码*.s文件。我们执行指令gcc -S Demo.i -o Demo.s,在当前目录下将生成Demo.s文件,用记事本打开会发现c源码已经被编译器转化为汇编代码。

Assembly.png

  如果使用指令gcc -S Demo.i即不指定输出文件名,默认也将会在当前目录下产生文件Demo.s。
  关于前面含有语法错误的Demo2.c我们在编译阶段再试一次,看看是否能检查出语法错误。

Compiling Error.png

  对语法的检查是再编译阶段进行的。

4.5 【指令】 -c(汇编(Assembling))

  执行-c(小写)指令将*.s文件中的汇编源码转化未机器能执行的二进制机器码,生成文件*.o。执行指令gcc -c Demo.s -o Demo.o。生成的Demo.o为二进制文件,用记事本打开就是一堆乱码,我这里就不贴图出来了。

4.6 【指令】 gcc *.o(链接(Linking))

  经过汇编处理后生成的二进制文件Demo.o虽然已经机器码,但仍然无法运行因为少了链接操作。链接操作可执行指令gcc Demo.o -o Demo.exe

Linking Error.png

  对Demo.o执行链接操作出现报错,提示找不到函数DoNothing的定义。预处理阶段值时将头文件包含进Demo.i文件中,也即Demo.i中含有函数的声明部分,所以编译阶段只检查函数的声明和调用处是否符合函数原型,并未去检查其他*.c文件中的函数定义。
  链接阶段就是要把函数库中的函数定义给关联进来,找不到函数定义当然会报错。那么问题来了,我们调用的标准库函数printf为何不报错?其实库函数也不例外,在链接阶段需要关联到函数定义,它的声明部分在标准库函数头文件stdio.h中已给出,而实现部分在某个标准库中封装好了,不同的系统下还会有差异。gcc在链接时默认是包含标准库的库文件路径的,所以它能找到printf函数的定义而不会报错。而DoNothing函数是我们用户自己编写,并未封装成库的形式gcc当然找不到它的定义。倘若把Demo.c中的DoNothing函数调用去掉可以成功执行链接操作吗?当然可以!
  为了让Demo.o能成功的生成可执行文件Demo.exe,我们可以将Test.c封装成静态库供其调用,这样就有函数定义了。执行指令gcc -c Test.c -o Test.o生成Test.o二进制文件。这里并不是不用对Test.c执行预处理和编译操作,而是编译器帮我们做了。接着将Test.o生成静态库文件libTest.a,然后再次尝试对Demo.o执行链接操作。

lib Test.png

  可以看到这次编译器没有报错且成功生成了Demo.exe,执行结果也与预期一直。
  关于生成静态库和动态库的gcc指令以及更多其他指令,静态库和动态库的区别和作用,等有时间了继续写个中级篇来讲解吧,一篇文章写得太长大家看了也会枯燥,我写着也需要休息。

  左侧选择“Basic Setup”选项,右侧勾选(点击“Mark for Installation”)需要安装的组件“mingw32-gcc-g++”。假设你也需要使用MinGW编译Fortran、Object-C、Ada语言的话也可以勾上相应的选项。选择好后点击左上角的Installation菜单中的Apply changes选项即开始在线下载安装相关组件。
  安装好后最好配置下环境变量,把MinGw的bin目录加入到环境变量的path中,这样dos在任意目录下都可以调用bin目录下的可执行文件,其他一些软件也能找到gcc的位置。cmd中输入gcc -v查看版本号。

浅显易懂的GCC使用教程

前言:
   2018-12-17天气暖,属于冬日里出太阳。最近在学习使用gvim,想着抛弃对IDE的依赖同时也是想了解编译的过程,但除了学习gvim繁多的指令外还得先学习使用gcc编译程序。这篇文章将会用浅显易懂的方式记录下gcc的基本使用,同时也不忘扩展探究。

  • 什么是GCC,它能干什么?
  • GCC、gcc、g++三者有何关系?
  • 开始开发前该做什么准备?
  • gcc常用指令讲解?

1 什么是gcc,它能干什么?

  GCC(GNU Compiler Collection)即GNU编译器套件,属于一种编程语言编译器,其原名为GCC(GNU C Compiler)即GNU c语言编译器,虽然缩写一样但是功能上区别很大。GCC的初衷是为GNU操作系统专门编写的一款编译器,原本的GNU是专用于编译C代码,现如今已扩展为可以编译C、C++、Java、Objective-C等多种编程语言的编译器集合了。这篇文章主要介绍gcc或g++的使用。

2 GCC、gcc、g++三者有何关系?

  gcc(GUN C Compiler)是GCC中的c编译器,而g++(GUN C++ Compiler)是GCC中的c++编译器。
  gcc和g++两者都可以编译c和cpp文件,但存在差异。gcc在编译cpp时语法按照c来编译但默认不能链接到c++的库(gcc默认链接c库,g++默认链接c++库)。g++编译.c和.cpp文件都统一按cpp的语法规则来编译。所以一般编译c用gcc,编译c++用g++。后文有时间会继续深入探讨区别。

3 开始开发前该做什么准备?

3.1 软件安装教程

  直接去官网上下载MinGW的包管理器。
  下载好后安装包管理器并运行它,可以看到如下界面:

Package Manager.png

  左侧选择“Basic Setup”选项,右侧勾选(点击“Mark for Installation”)需要安装的组件“mingw32-gcc-g++”。假设你也需要使用MinGW编译Fortran、Object-C、Ada语言的话也可以勾上相应的选项。选择好后点击左上角的Installation菜单中的Apply changes选项即开始在线下载安装相关组件。
  安装好后最好配置下环境变量,把MinGw的bin目录加入到环境变量的path中,这样dos在任意目录下都可以调用bin目录下的可执行文件,其他一些软件也能找到gcc的位置。cmd中输入gcc -v查看版本号。

Version.png

4 gcc常用指令讲解?

  用gcc在Windows上编译*.c文件并非直接生成*.exe文件(Linux上为*.out),中间还经历了预处理、编译和汇编几个过程,好在gcc提供了生成中间文件的指令,虽然平时开发的时候很少关注编译过程中生成的这些*.i和*.s文件,但对其有一定的了解总归是好的。

Process.png

4.1 gcc指令

  使用gcc指令编译*.c文件可以生成*exe可执行文件。下面我们用一段简单的c代码来讲解,Demo.c内源码如下:

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

int main(void)
{
printf("nihao\n");

return 0;
}

  打开cmd,需要注意的是cmd默认路径一般是c盘,而Demo.c文件可能并不在cmd的默认路径下,我们需要使用dos指令cd到Demo.c所在目录下才能对文件进行操作。

Order gcc.png

  对Demo.c文件执行gcc指令后会在该目录下生成可执行文件a.exe,由于没有制定exe的文件名所以默认为a.exe。

a exe.png

  当然也不一定要cd进Demo.c的目录,使用gcc指令时给出Demo.c的绝对路径也可以成功编译,命令如下:

cmd
1
gcc E:\WorkSpace\Test_gcc\Demo.c

4.2 【指令】 -o

  指令-o(小写)用来指定生成的文件名。

Order o.png

结果如下:

Demo exe.png

  生成的文件不一定要在.c所在目录,可以给出路径指定:

cmd
1
gcc Demo.c -o ..\Demo.exe

  该指令指定将exe生成到上一级目录。
  生成exe后我们试者执行一下看看结果。

Run.png

4.3 【指令】 -E(预处理(Preprocessing))

  指令 -E(大写)将执行预处理操作也即生成*.i文件,gcc编译器将对#开头的指令进行解析。我们修改Demo.c中的代码为如下:

c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <stdlib.h>
#include "Test.h"

int main()
{
int a = N; //宏常量
int b = 2;
int c = 0;

c = a + b;
printf("%d\n", c);

CODE //宏替换代码段

DoNothing();

system("pause");
return 0;
}

  在Demo.c同目录下编辑Test.h文件,源码如下:

c
1
2
3
4
5
6
7
8
#define N 1

#define CODE if(c > 2) \
{ \
printf("c > 2\n"); \
}

void DoNothing(void); //函数声明(该函数未被调用)

  在Demo.c同目录下编辑Test.c文件,源码如下:

c
1
2
3
4
5
6
7
8
#include"Test.h"

void DoNothing(void)
{
;

return ;
}

  从源码可知Demo.c中在预处理阶段需要把调用的头文件包含进来,替换宏常量和宏代码段。我们执行指令。

cmd
1
gcc -E Demo.c -o Demo.i

  并看看Demo.i文件中的代码,Demo.i文件内容如下。

Order E.png

  -E 指令使gcc执行预处理,预处理时对#类指令进行处理(包含头文件、替换宏常量和宏代码段等操作),也就不难理解为何Demo.i文件会很大了(38KB),整个工程最后被揉合成一个大文件。预处理把注释都去掉了,所以反编译回来的代码是没有注释的!因为注释早就被去掉了。另外可以一提的是使用指令gcc -E Demo.c不指定输出的文件名时内容将会直接输出到Dos框中,而不会产生文件。
  我们知道c语言出现语法错误时编译器将会报错,但检查语法错误是在预处理、编译、汇编、链接、生成可执行文件中的哪个阶段执行的呢?为了方便我们编辑Demo2.c代码,有意使源码代码由于错误测试预处理阶段是否会报语法错误。

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

int main(void)
{
aabbccdd //此处有语法错误

return 0;
}

  对Demo2.c执行预处理操作gcc -E Demo2.c

Preprocessing Error.png

  由此可知预处理阶段不检查语法错误。

4.4 【指令】 -S(编译(Compiling))

  执行-S(大写)指令将*.i文件中源码转化为汇编代码*.s文件。我们执行指令gcc -S Demo.i -o Demo.s,在当前目录下将生成Demo.s文件,用记事本打开会发现c源码已经被编译器转化为汇编代码。

Assembly.png

  如果使用指令gcc -S Demo.i即不指定输出文件名,默认也将会在当前目录下产生文件Demo.s。
  关于前面含有语法错误的Demo2.c我们在编译阶段再试一次,看看是否能检查出语法错误。

Compiling Error.png

  对语法的检查是再编译阶段进行的。

4.5 【指令】 -c(汇编(Assembling))

  执行-c(小写)指令将*.s文件中的汇编源码转化未机器能执行的二进制机器码,生成文件*.o。执行指令gcc -c Demo.s -o Demo.o。生成的Demo.o为二进制文件,用记事本打开就是一堆乱码,我这里就不贴图出来了。

4.6 【指令】 gcc *.o(链接(Linking))

  经过汇编处理后生成的二进制文件Demo.o虽然已经机器码,但仍然无法运行因为少了链接操作。链接操作可执行指令gcc Demo.o -o Demo.exe

Linking Error.png

  对Demo.o执行链接操作出现报错,提示找不到函数DoNothing的定义。预处理阶段值时将头文件包含进Demo.i文件中,也即Demo.i中含有函数的声明部分,所以编译阶段只检查函数的声明和调用处是否符合函数原型,并未去检查其他*.c文件中的函数定义。
  链接阶段就是要把函数库中的函数定义给关联进来,找不到函数定义当然会报错。那么问题来了,我们调用的标准库函数printf为何不报错?其实库函数也不例外,在链接阶段需要关联到函数定义,它的声明部分在标准库函数头文件stdio.h中已给出,而实现部分在某个标准库中封装好了,不同的系统下还会有差异。gcc在链接时默认是包含标准库的库文件路径的,所以它能找到printf函数的定义而不会报错。而DoNothing函数是我们用户自己编写,并未封装成库的形式gcc当然找不到它的定义。倘若把Demo.c中的DoNothing函数调用去掉可以成功执行链接操作吗?当然可以!
  为了让Demo.o能成功的生成可执行文件Demo.exe,我们可以将Test.c封装成静态库供其调用,这样就有函数定义了。执行指令gcc -c Test.c -o Test.o生成Test.o二进制文件。这里并不是不用对Test.c执行预处理和编译操作,而是编译器帮我们做了。接着将Test.o生成静态库文件libTest.a,然后再次尝试对Demo.o执行链接操作。

lib Test.png

  可以看到这次编译器没有报错且成功生成了Demo.exe,执行结果也与预期一直。
  关于生成静态库和动态库的gcc指令以及更多其他指令,静态库和动态库的区别和作用,等有时间了继续写个中级篇来讲解吧,一篇文章写得太长大家看了也会枯燥,我写着也需要休息。

4 gcc常用指令讲解?

  用gcc在Windows上编译*.c文件并非直接生成*.exe文件(Linux上为*.out),中间还经历了预处理、编译和汇编几个过程,好在gcc提供了生成中间文件的指令,虽然平时开发的时候很少关注编译过程中生成的这些*.i和*.s文件,但对其有一定的了解总归是好的。

浅显易懂的GCC使用教程

前言:
   2018-12-17天气暖,属于冬日里出太阳。最近在学习使用gvim,想着抛弃对IDE的依赖同时也是想了解编译的过程,但除了学习gvim繁多的指令外还得先学习使用gcc编译程序。这篇文章将会用浅显易懂的方式记录下gcc的基本使用,同时也不忘扩展探究。

  • 什么是GCC,它能干什么?
  • GCC、gcc、g++三者有何关系?
  • 开始开发前该做什么准备?
  • gcc常用指令讲解?

1 什么是gcc,它能干什么?

  GCC(GNU Compiler Collection)即GNU编译器套件,属于一种编程语言编译器,其原名为GCC(GNU C Compiler)即GNU c语言编译器,虽然缩写一样但是功能上区别很大。GCC的初衷是为GNU操作系统专门编写的一款编译器,原本的GNU是专用于编译C代码,现如今已扩展为可以编译C、C++、Java、Objective-C等多种编程语言的编译器集合了。这篇文章主要介绍gcc或g++的使用。

2 GCC、gcc、g++三者有何关系?

  gcc(GUN C Compiler)是GCC中的c编译器,而g++(GUN C++ Compiler)是GCC中的c++编译器。
  gcc和g++两者都可以编译c和cpp文件,但存在差异。gcc在编译cpp时语法按照c来编译但默认不能链接到c++的库(gcc默认链接c库,g++默认链接c++库)。g++编译.c和.cpp文件都统一按cpp的语法规则来编译。所以一般编译c用gcc,编译c++用g++。后文有时间会继续深入探讨区别。

3 开始开发前该做什么准备?

3.1 软件安装教程

  直接去官网上下载MinGW的包管理器。
  下载好后安装包管理器并运行它,可以看到如下界面:

Package Manager.png

  左侧选择“Basic Setup”选项,右侧勾选(点击“Mark for Installation”)需要安装的组件“mingw32-gcc-g++”。假设你也需要使用MinGW编译Fortran、Object-C、Ada语言的话也可以勾上相应的选项。选择好后点击左上角的Installation菜单中的Apply changes选项即开始在线下载安装相关组件。
  安装好后最好配置下环境变量,把MinGw的bin目录加入到环境变量的path中,这样dos在任意目录下都可以调用bin目录下的可执行文件,其他一些软件也能找到gcc的位置。cmd中输入gcc -v查看版本号。

Version.png

4 gcc常用指令讲解?

  用gcc在Windows上编译*.c文件并非直接生成*.exe文件(Linux上为*.out),中间还经历了预处理、编译和汇编几个过程,好在gcc提供了生成中间文件的指令,虽然平时开发的时候很少关注编译过程中生成的这些*.i和*.s文件,但对其有一定的了解总归是好的。

Process.png

4.1 gcc指令

  使用gcc指令编译*.c文件可以生成*exe可执行文件。下面我们用一段简单的c代码来讲解,Demo.c内源码如下:

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

int main(void)
{
printf("nihao\n");

return 0;
}

  打开cmd,需要注意的是cmd默认路径一般是c盘,而Demo.c文件可能并不在cmd的默认路径下,我们需要使用dos指令cd到Demo.c所在目录下才能对文件进行操作。

Order gcc.png

  对Demo.c文件执行gcc指令后会在该目录下生成可执行文件a.exe,由于没有制定exe的文件名所以默认为a.exe。

a exe.png

  当然也不一定要cd进Demo.c的目录,使用gcc指令时给出Demo.c的绝对路径也可以成功编译,命令如下:

cmd
1
gcc E:\WorkSpace\Test_gcc\Demo.c

4.2 【指令】 -o

  指令-o(小写)用来指定生成的文件名。

Order o.png

结果如下:

Demo exe.png

  生成的文件不一定要在.c所在目录,可以给出路径指定:

cmd
1
gcc Demo.c -o ..\Demo.exe

  该指令指定将exe生成到上一级目录。
  生成exe后我们试者执行一下看看结果。

Run.png

4.3 【指令】 -E(预处理(Preprocessing))

  指令 -E(大写)将执行预处理操作也即生成*.i文件,gcc编译器将对#开头的指令进行解析。我们修改Demo.c中的代码为如下:

c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <stdlib.h>
#include "Test.h"

int main()
{
int a = N; //宏常量
int b = 2;
int c = 0;

c = a + b;
printf("%d\n", c);

CODE //宏替换代码段

DoNothing();

system("pause");
return 0;
}

  在Demo.c同目录下编辑Test.h文件,源码如下:

c
1
2
3
4
5
6
7
8
#define N 1

#define CODE if(c > 2) \
{ \
printf("c > 2\n"); \
}

void DoNothing(void); //函数声明(该函数未被调用)

  在Demo.c同目录下编辑Test.c文件,源码如下:

c
1
2
3
4
5
6
7
8
#include"Test.h"

void DoNothing(void)
{
;

return ;
}

  从源码可知Demo.c中在预处理阶段需要把调用的头文件包含进来,替换宏常量和宏代码段。我们执行指令。

cmd
1
gcc -E Demo.c -o Demo.i

  并看看Demo.i文件中的代码,Demo.i文件内容如下。

Order E.png

  -E 指令使gcc执行预处理,预处理时对#类指令进行处理(包含头文件、替换宏常量和宏代码段等操作),也就不难理解为何Demo.i文件会很大了(38KB),整个工程最后被揉合成一个大文件。预处理把注释都去掉了,所以反编译回来的代码是没有注释的!因为注释早就被去掉了。另外可以一提的是使用指令gcc -E Demo.c不指定输出的文件名时内容将会直接输出到Dos框中,而不会产生文件。
  我们知道c语言出现语法错误时编译器将会报错,但检查语法错误是在预处理、编译、汇编、链接、生成可执行文件中的哪个阶段执行的呢?为了方便我们编辑Demo2.c代码,有意使源码代码由于错误测试预处理阶段是否会报语法错误。

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

int main(void)
{
aabbccdd //此处有语法错误

return 0;
}

  对Demo2.c执行预处理操作gcc -E Demo2.c

Preprocessing Error.png

  由此可知预处理阶段不检查语法错误。

4.4 【指令】 -S(编译(Compiling))

  执行-S(大写)指令将*.i文件中源码转化为汇编代码*.s文件。我们执行指令gcc -S Demo.i -o Demo.s,在当前目录下将生成Demo.s文件,用记事本打开会发现c源码已经被编译器转化为汇编代码。

Assembly.png

  如果使用指令gcc -S Demo.i即不指定输出文件名,默认也将会在当前目录下产生文件Demo.s。
  关于前面含有语法错误的Demo2.c我们在编译阶段再试一次,看看是否能检查出语法错误。

Compiling Error.png

  对语法的检查是再编译阶段进行的。

4.5 【指令】 -c(汇编(Assembling))

  执行-c(小写)指令将*.s文件中的汇编源码转化未机器能执行的二进制机器码,生成文件*.o。执行指令gcc -c Demo.s -o Demo.o。生成的Demo.o为二进制文件,用记事本打开就是一堆乱码,我这里就不贴图出来了。

4.6 【指令】 gcc *.o(链接(Linking))

  经过汇编处理后生成的二进制文件Demo.o虽然已经机器码,但仍然无法运行因为少了链接操作。链接操作可执行指令gcc Demo.o -o Demo.exe

Linking Error.png

  对Demo.o执行链接操作出现报错,提示找不到函数DoNothing的定义。预处理阶段值时将头文件包含进Demo.i文件中,也即Demo.i中含有函数的声明部分,所以编译阶段只检查函数的声明和调用处是否符合函数原型,并未去检查其他*.c文件中的函数定义。
  链接阶段就是要把函数库中的函数定义给关联进来,找不到函数定义当然会报错。那么问题来了,我们调用的标准库函数printf为何不报错?其实库函数也不例外,在链接阶段需要关联到函数定义,它的声明部分在标准库函数头文件stdio.h中已给出,而实现部分在某个标准库中封装好了,不同的系统下还会有差异。gcc在链接时默认是包含标准库的库文件路径的,所以它能找到printf函数的定义而不会报错。而DoNothing函数是我们用户自己编写,并未封装成库的形式gcc当然找不到它的定义。倘若把Demo.c中的DoNothing函数调用去掉可以成功执行链接操作吗?当然可以!
  为了让Demo.o能成功的生成可执行文件Demo.exe,我们可以将Test.c封装成静态库供其调用,这样就有函数定义了。执行指令gcc -c Test.c -o Test.o生成Test.o二进制文件。这里并不是不用对Test.c执行预处理和编译操作,而是编译器帮我们做了。接着将Test.o生成静态库文件libTest.a,然后再次尝试对Demo.o执行链接操作。

lib Test.png

  可以看到这次编译器没有报错且成功生成了Demo.exe,执行结果也与预期一直。
  关于生成静态库和动态库的gcc指令以及更多其他指令,静态库和动态库的区别和作用,等有时间了继续写个中级篇来讲解吧,一篇文章写得太长大家看了也会枯燥,我写着也需要休息。

4.1 gcc指令

  使用gcc指令编译*.c文件可以生成*exe可执行文件。下面我们用一段简单的c代码来讲解,Demo.c内源码如下:

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

int main(void)
{
printf("nihao\n");

return 0;
}

  打开cmd,需要注意的是cmd默认路径一般是c盘,而Demo.c文件可能并不在cmd的默认路径下,我们需要使用dos指令cd到Demo.c所在目录下才能对文件进行操作。

浅显易懂的GCC使用教程

前言:
   2018-12-17天气暖,属于冬日里出太阳。最近在学习使用gvim,想着抛弃对IDE的依赖同时也是想了解编译的过程,但除了学习gvim繁多的指令外还得先学习使用gcc编译程序。这篇文章将会用浅显易懂的方式记录下gcc的基本使用,同时也不忘扩展探究。

  • 什么是GCC,它能干什么?
  • GCC、gcc、g++三者有何关系?
  • 开始开发前该做什么准备?
  • gcc常用指令讲解?

1 什么是gcc,它能干什么?

  GCC(GNU Compiler Collection)即GNU编译器套件,属于一种编程语言编译器,其原名为GCC(GNU C Compiler)即GNU c语言编译器,虽然缩写一样但是功能上区别很大。GCC的初衷是为GNU操作系统专门编写的一款编译器,原本的GNU是专用于编译C代码,现如今已扩展为可以编译C、C++、Java、Objective-C等多种编程语言的编译器集合了。这篇文章主要介绍gcc或g++的使用。

2 GCC、gcc、g++三者有何关系?

  gcc(GUN C Compiler)是GCC中的c编译器,而g++(GUN C++ Compiler)是GCC中的c++编译器。
  gcc和g++两者都可以编译c和cpp文件,但存在差异。gcc在编译cpp时语法按照c来编译但默认不能链接到c++的库(gcc默认链接c库,g++默认链接c++库)。g++编译.c和.cpp文件都统一按cpp的语法规则来编译。所以一般编译c用gcc,编译c++用g++。后文有时间会继续深入探讨区别。

3 开始开发前该做什么准备?

3.1 软件安装教程

  直接去官网上下载MinGW的包管理器。
  下载好后安装包管理器并运行它,可以看到如下界面:

Package Manager.png

  左侧选择“Basic Setup”选项,右侧勾选(点击“Mark for Installation”)需要安装的组件“mingw32-gcc-g++”。假设你也需要使用MinGW编译Fortran、Object-C、Ada语言的话也可以勾上相应的选项。选择好后点击左上角的Installation菜单中的Apply changes选项即开始在线下载安装相关组件。
  安装好后最好配置下环境变量,把MinGw的bin目录加入到环境变量的path中,这样dos在任意目录下都可以调用bin目录下的可执行文件,其他一些软件也能找到gcc的位置。cmd中输入gcc -v查看版本号。

Version.png

4 gcc常用指令讲解?

  用gcc在Windows上编译*.c文件并非直接生成*.exe文件(Linux上为*.out),中间还经历了预处理、编译和汇编几个过程,好在gcc提供了生成中间文件的指令,虽然平时开发的时候很少关注编译过程中生成的这些*.i和*.s文件,但对其有一定的了解总归是好的。

Process.png

4.1 gcc指令

  使用gcc指令编译*.c文件可以生成*exe可执行文件。下面我们用一段简单的c代码来讲解,Demo.c内源码如下:

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

int main(void)
{
printf("nihao\n");

return 0;
}

  打开cmd,需要注意的是cmd默认路径一般是c盘,而Demo.c文件可能并不在cmd的默认路径下,我们需要使用dos指令cd到Demo.c所在目录下才能对文件进行操作。

Order gcc.png

  对Demo.c文件执行gcc指令后会在该目录下生成可执行文件a.exe,由于没有制定exe的文件名所以默认为a.exe。

a exe.png

  当然也不一定要cd进Demo.c的目录,使用gcc指令时给出Demo.c的绝对路径也可以成功编译,命令如下:

cmd
1
gcc E:\WorkSpace\Test_gcc\Demo.c

4.2 【指令】 -o

  指令-o(小写)用来指定生成的文件名。

Order o.png

结果如下:

Demo exe.png

  生成的文件不一定要在.c所在目录,可以给出路径指定:

cmd
1
gcc Demo.c -o ..\Demo.exe

  该指令指定将exe生成到上一级目录。
  生成exe后我们试者执行一下看看结果。

Run.png

4.3 【指令】 -E(预处理(Preprocessing))

  指令 -E(大写)将执行预处理操作也即生成*.i文件,gcc编译器将对#开头的指令进行解析。我们修改Demo.c中的代码为如下:

c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <stdlib.h>
#include "Test.h"

int main()
{
int a = N; //宏常量
int b = 2;
int c = 0;

c = a + b;
printf("%d\n", c);

CODE //宏替换代码段

DoNothing();

system("pause");
return 0;
}

  在Demo.c同目录下编辑Test.h文件,源码如下:

c
1
2
3
4
5
6
7
8
#define N 1

#define CODE if(c > 2) \
{ \
printf("c > 2\n"); \
}

void DoNothing(void); //函数声明(该函数未被调用)

  在Demo.c同目录下编辑Test.c文件,源码如下:

c
1
2
3
4
5
6
7
8
#include"Test.h"

void DoNothing(void)
{
;

return ;
}

  从源码可知Demo.c中在预处理阶段需要把调用的头文件包含进来,替换宏常量和宏代码段。我们执行指令。

cmd
1
gcc -E Demo.c -o Demo.i

  并看看Demo.i文件中的代码,Demo.i文件内容如下。

Order E.png

  -E 指令使gcc执行预处理,预处理时对#类指令进行处理(包含头文件、替换宏常量和宏代码段等操作),也就不难理解为何Demo.i文件会很大了(38KB),整个工程最后被揉合成一个大文件。预处理把注释都去掉了,所以反编译回来的代码是没有注释的!因为注释早就被去掉了。另外可以一提的是使用指令gcc -E Demo.c不指定输出的文件名时内容将会直接输出到Dos框中,而不会产生文件。
  我们知道c语言出现语法错误时编译器将会报错,但检查语法错误是在预处理、编译、汇编、链接、生成可执行文件中的哪个阶段执行的呢?为了方便我们编辑Demo2.c代码,有意使源码代码由于错误测试预处理阶段是否会报语法错误。

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

int main(void)
{
aabbccdd //此处有语法错误

return 0;
}

  对Demo2.c执行预处理操作gcc -E Demo2.c

Preprocessing Error.png

  由此可知预处理阶段不检查语法错误。

4.4 【指令】 -S(编译(Compiling))

  执行-S(大写)指令将*.i文件中源码转化为汇编代码*.s文件。我们执行指令gcc -S Demo.i -o Demo.s,在当前目录下将生成Demo.s文件,用记事本打开会发现c源码已经被编译器转化为汇编代码。

Assembly.png

  如果使用指令gcc -S Demo.i即不指定输出文件名,默认也将会在当前目录下产生文件Demo.s。
  关于前面含有语法错误的Demo2.c我们在编译阶段再试一次,看看是否能检查出语法错误。

Compiling Error.png

  对语法的检查是再编译阶段进行的。

4.5 【指令】 -c(汇编(Assembling))

  执行-c(小写)指令将*.s文件中的汇编源码转化未机器能执行的二进制机器码,生成文件*.o。执行指令gcc -c Demo.s -o Demo.o。生成的Demo.o为二进制文件,用记事本打开就是一堆乱码,我这里就不贴图出来了。

4.6 【指令】 gcc *.o(链接(Linking))

  经过汇编处理后生成的二进制文件Demo.o虽然已经机器码,但仍然无法运行因为少了链接操作。链接操作可执行指令gcc Demo.o -o Demo.exe

Linking Error.png

  对Demo.o执行链接操作出现报错,提示找不到函数DoNothing的定义。预处理阶段值时将头文件包含进Demo.i文件中,也即Demo.i中含有函数的声明部分,所以编译阶段只检查函数的声明和调用处是否符合函数原型,并未去检查其他*.c文件中的函数定义。
  链接阶段就是要把函数库中的函数定义给关联进来,找不到函数定义当然会报错。那么问题来了,我们调用的标准库函数printf为何不报错?其实库函数也不例外,在链接阶段需要关联到函数定义,它的声明部分在标准库函数头文件stdio.h中已给出,而实现部分在某个标准库中封装好了,不同的系统下还会有差异。gcc在链接时默认是包含标准库的库文件路径的,所以它能找到printf函数的定义而不会报错。而DoNothing函数是我们用户自己编写,并未封装成库的形式gcc当然找不到它的定义。倘若把Demo.c中的DoNothing函数调用去掉可以成功执行链接操作吗?当然可以!
  为了让Demo.o能成功的生成可执行文件Demo.exe,我们可以将Test.c封装成静态库供其调用,这样就有函数定义了。执行指令gcc -c Test.c -o Test.o生成Test.o二进制文件。这里并不是不用对Test.c执行预处理和编译操作,而是编译器帮我们做了。接着将Test.o生成静态库文件libTest.a,然后再次尝试对Demo.o执行链接操作。

lib Test.png

  可以看到这次编译器没有报错且成功生成了Demo.exe,执行结果也与预期一直。
  关于生成静态库和动态库的gcc指令以及更多其他指令,静态库和动态库的区别和作用,等有时间了继续写个中级篇来讲解吧,一篇文章写得太长大家看了也会枯燥,我写着也需要休息。

  对Demo.c文件执行gcc指令后会在该目录下生成可执行文件a.exe,由于没有制定exe的文件名所以默认为a.exe。

浅显易懂的GCC使用教程

前言:
   2018-12-17天气暖,属于冬日里出太阳。最近在学习使用gvim,想着抛弃对IDE的依赖同时也是想了解编译的过程,但除了学习gvim繁多的指令外还得先学习使用gcc编译程序。这篇文章将会用浅显易懂的方式记录下gcc的基本使用,同时也不忘扩展探究。

  • 什么是GCC,它能干什么?
  • GCC、gcc、g++三者有何关系?
  • 开始开发前该做什么准备?
  • gcc常用指令讲解?

1 什么是gcc,它能干什么?

  GCC(GNU Compiler Collection)即GNU编译器套件,属于一种编程语言编译器,其原名为GCC(GNU C Compiler)即GNU c语言编译器,虽然缩写一样但是功能上区别很大。GCC的初衷是为GNU操作系统专门编写的一款编译器,原本的GNU是专用于编译C代码,现如今已扩展为可以编译C、C++、Java、Objective-C等多种编程语言的编译器集合了。这篇文章主要介绍gcc或g++的使用。

2 GCC、gcc、g++三者有何关系?

  gcc(GUN C Compiler)是GCC中的c编译器,而g++(GUN C++ Compiler)是GCC中的c++编译器。
  gcc和g++两者都可以编译c和cpp文件,但存在差异。gcc在编译cpp时语法按照c来编译但默认不能链接到c++的库(gcc默认链接c库,g++默认链接c++库)。g++编译.c和.cpp文件都统一按cpp的语法规则来编译。所以一般编译c用gcc,编译c++用g++。后文有时间会继续深入探讨区别。

3 开始开发前该做什么准备?

3.1 软件安装教程

  直接去官网上下载MinGW的包管理器。
  下载好后安装包管理器并运行它,可以看到如下界面:

Package Manager.png

  左侧选择“Basic Setup”选项,右侧勾选(点击“Mark for Installation”)需要安装的组件“mingw32-gcc-g++”。假设你也需要使用MinGW编译Fortran、Object-C、Ada语言的话也可以勾上相应的选项。选择好后点击左上角的Installation菜单中的Apply changes选项即开始在线下载安装相关组件。
  安装好后最好配置下环境变量,把MinGw的bin目录加入到环境变量的path中,这样dos在任意目录下都可以调用bin目录下的可执行文件,其他一些软件也能找到gcc的位置。cmd中输入gcc -v查看版本号。

Version.png

4 gcc常用指令讲解?

  用gcc在Windows上编译*.c文件并非直接生成*.exe文件(Linux上为*.out),中间还经历了预处理、编译和汇编几个过程,好在gcc提供了生成中间文件的指令,虽然平时开发的时候很少关注编译过程中生成的这些*.i和*.s文件,但对其有一定的了解总归是好的。

Process.png

4.1 gcc指令

  使用gcc指令编译*.c文件可以生成*exe可执行文件。下面我们用一段简单的c代码来讲解,Demo.c内源码如下:

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

int main(void)
{
printf("nihao\n");

return 0;
}

  打开cmd,需要注意的是cmd默认路径一般是c盘,而Demo.c文件可能并不在cmd的默认路径下,我们需要使用dos指令cd到Demo.c所在目录下才能对文件进行操作。

Order gcc.png

  对Demo.c文件执行gcc指令后会在该目录下生成可执行文件a.exe,由于没有制定exe的文件名所以默认为a.exe。

a exe.png

  当然也不一定要cd进Demo.c的目录,使用gcc指令时给出Demo.c的绝对路径也可以成功编译,命令如下:

cmd
1
gcc E:\WorkSpace\Test_gcc\Demo.c

4.2 【指令】 -o

  指令-o(小写)用来指定生成的文件名。

Order o.png

结果如下:

Demo exe.png

  生成的文件不一定要在.c所在目录,可以给出路径指定:

cmd
1
gcc Demo.c -o ..\Demo.exe

  该指令指定将exe生成到上一级目录。
  生成exe后我们试者执行一下看看结果。

Run.png

4.3 【指令】 -E(预处理(Preprocessing))

  指令 -E(大写)将执行预处理操作也即生成*.i文件,gcc编译器将对#开头的指令进行解析。我们修改Demo.c中的代码为如下:

c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <stdlib.h>
#include "Test.h"

int main()
{
int a = N; //宏常量
int b = 2;
int c = 0;

c = a + b;
printf("%d\n", c);

CODE //宏替换代码段

DoNothing();

system("pause");
return 0;
}

  在Demo.c同目录下编辑Test.h文件,源码如下:

c
1
2
3
4
5
6
7
8
#define N 1

#define CODE if(c > 2) \
{ \
printf("c > 2\n"); \
}

void DoNothing(void); //函数声明(该函数未被调用)

  在Demo.c同目录下编辑Test.c文件,源码如下:

c
1
2
3
4
5
6
7
8
#include"Test.h"

void DoNothing(void)
{
;

return ;
}

  从源码可知Demo.c中在预处理阶段需要把调用的头文件包含进来,替换宏常量和宏代码段。我们执行指令。

cmd
1
gcc -E Demo.c -o Demo.i

  并看看Demo.i文件中的代码,Demo.i文件内容如下。

Order E.png

  -E 指令使gcc执行预处理,预处理时对#类指令进行处理(包含头文件、替换宏常量和宏代码段等操作),也就不难理解为何Demo.i文件会很大了(38KB),整个工程最后被揉合成一个大文件。预处理把注释都去掉了,所以反编译回来的代码是没有注释的!因为注释早就被去掉了。另外可以一提的是使用指令gcc -E Demo.c不指定输出的文件名时内容将会直接输出到Dos框中,而不会产生文件。
  我们知道c语言出现语法错误时编译器将会报错,但检查语法错误是在预处理、编译、汇编、链接、生成可执行文件中的哪个阶段执行的呢?为了方便我们编辑Demo2.c代码,有意使源码代码由于错误测试预处理阶段是否会报语法错误。

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

int main(void)
{
aabbccdd //此处有语法错误

return 0;
}

  对Demo2.c执行预处理操作gcc -E Demo2.c

Preprocessing Error.png

  由此可知预处理阶段不检查语法错误。

4.4 【指令】 -S(编译(Compiling))

  执行-S(大写)指令将*.i文件中源码转化为汇编代码*.s文件。我们执行指令gcc -S Demo.i -o Demo.s,在当前目录下将生成Demo.s文件,用记事本打开会发现c源码已经被编译器转化为汇编代码。

Assembly.png

  如果使用指令gcc -S Demo.i即不指定输出文件名,默认也将会在当前目录下产生文件Demo.s。
  关于前面含有语法错误的Demo2.c我们在编译阶段再试一次,看看是否能检查出语法错误。

Compiling Error.png

  对语法的检查是再编译阶段进行的。

4.5 【指令】 -c(汇编(Assembling))

  执行-c(小写)指令将*.s文件中的汇编源码转化未机器能执行的二进制机器码,生成文件*.o。执行指令gcc -c Demo.s -o Demo.o。生成的Demo.o为二进制文件,用记事本打开就是一堆乱码,我这里就不贴图出来了。

4.6 【指令】 gcc *.o(链接(Linking))

  经过汇编处理后生成的二进制文件Demo.o虽然已经机器码,但仍然无法运行因为少了链接操作。链接操作可执行指令gcc Demo.o -o Demo.exe

Linking Error.png

  对Demo.o执行链接操作出现报错,提示找不到函数DoNothing的定义。预处理阶段值时将头文件包含进Demo.i文件中,也即Demo.i中含有函数的声明部分,所以编译阶段只检查函数的声明和调用处是否符合函数原型,并未去检查其他*.c文件中的函数定义。
  链接阶段就是要把函数库中的函数定义给关联进来,找不到函数定义当然会报错。那么问题来了,我们调用的标准库函数printf为何不报错?其实库函数也不例外,在链接阶段需要关联到函数定义,它的声明部分在标准库函数头文件stdio.h中已给出,而实现部分在某个标准库中封装好了,不同的系统下还会有差异。gcc在链接时默认是包含标准库的库文件路径的,所以它能找到printf函数的定义而不会报错。而DoNothing函数是我们用户自己编写,并未封装成库的形式gcc当然找不到它的定义。倘若把Demo.c中的DoNothing函数调用去掉可以成功执行链接操作吗?当然可以!
  为了让Demo.o能成功的生成可执行文件Demo.exe,我们可以将Test.c封装成静态库供其调用,这样就有函数定义了。执行指令gcc -c Test.c -o Test.o生成Test.o二进制文件。这里并不是不用对Test.c执行预处理和编译操作,而是编译器帮我们做了。接着将Test.o生成静态库文件libTest.a,然后再次尝试对Demo.o执行链接操作。

lib Test.png

  可以看到这次编译器没有报错且成功生成了Demo.exe,执行结果也与预期一直。
  关于生成静态库和动态库的gcc指令以及更多其他指令,静态库和动态库的区别和作用,等有时间了继续写个中级篇来讲解吧,一篇文章写得太长大家看了也会枯燥,我写着也需要休息。

  当然也不一定要cd进Demo.c的目录,使用gcc指令时给出Demo.c的绝对路径也可以成功编译,命令如下:

cmd
1
gcc E:\WorkSpace\Test_gcc\Demo.c

4.2 【指令】 -o

  指令-o(小写)用来指定生成的文件名。

浅显易懂的GCC使用教程

前言:
   2018-12-17天气暖,属于冬日里出太阳。最近在学习使用gvim,想着抛弃对IDE的依赖同时也是想了解编译的过程,但除了学习gvim繁多的指令外还得先学习使用gcc编译程序。这篇文章将会用浅显易懂的方式记录下gcc的基本使用,同时也不忘扩展探究。

  • 什么是GCC,它能干什么?
  • GCC、gcc、g++三者有何关系?
  • 开始开发前该做什么准备?
  • gcc常用指令讲解?

1 什么是gcc,它能干什么?

  GCC(GNU Compiler Collection)即GNU编译器套件,属于一种编程语言编译器,其原名为GCC(GNU C Compiler)即GNU c语言编译器,虽然缩写一样但是功能上区别很大。GCC的初衷是为GNU操作系统专门编写的一款编译器,原本的GNU是专用于编译C代码,现如今已扩展为可以编译C、C++、Java、Objective-C等多种编程语言的编译器集合了。这篇文章主要介绍gcc或g++的使用。

2 GCC、gcc、g++三者有何关系?

  gcc(GUN C Compiler)是GCC中的c编译器,而g++(GUN C++ Compiler)是GCC中的c++编译器。
  gcc和g++两者都可以编译c和cpp文件,但存在差异。gcc在编译cpp时语法按照c来编译但默认不能链接到c++的库(gcc默认链接c库,g++默认链接c++库)。g++编译.c和.cpp文件都统一按cpp的语法规则来编译。所以一般编译c用gcc,编译c++用g++。后文有时间会继续深入探讨区别。

3 开始开发前该做什么准备?

3.1 软件安装教程

  直接去官网上下载MinGW的包管理器。
  下载好后安装包管理器并运行它,可以看到如下界面:

Package Manager.png

  左侧选择“Basic Setup”选项,右侧勾选(点击“Mark for Installation”)需要安装的组件“mingw32-gcc-g++”。假设你也需要使用MinGW编译Fortran、Object-C、Ada语言的话也可以勾上相应的选项。选择好后点击左上角的Installation菜单中的Apply changes选项即开始在线下载安装相关组件。
  安装好后最好配置下环境变量,把MinGw的bin目录加入到环境变量的path中,这样dos在任意目录下都可以调用bin目录下的可执行文件,其他一些软件也能找到gcc的位置。cmd中输入gcc -v查看版本号。

Version.png

4 gcc常用指令讲解?

  用gcc在Windows上编译*.c文件并非直接生成*.exe文件(Linux上为*.out),中间还经历了预处理、编译和汇编几个过程,好在gcc提供了生成中间文件的指令,虽然平时开发的时候很少关注编译过程中生成的这些*.i和*.s文件,但对其有一定的了解总归是好的。

Process.png

4.1 gcc指令

  使用gcc指令编译*.c文件可以生成*exe可执行文件。下面我们用一段简单的c代码来讲解,Demo.c内源码如下:

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

int main(void)
{
printf("nihao\n");

return 0;
}

  打开cmd,需要注意的是cmd默认路径一般是c盘,而Demo.c文件可能并不在cmd的默认路径下,我们需要使用dos指令cd到Demo.c所在目录下才能对文件进行操作。

Order gcc.png

  对Demo.c文件执行gcc指令后会在该目录下生成可执行文件a.exe,由于没有制定exe的文件名所以默认为a.exe。

a exe.png

  当然也不一定要cd进Demo.c的目录,使用gcc指令时给出Demo.c的绝对路径也可以成功编译,命令如下:

cmd
1
gcc E:\WorkSpace\Test_gcc\Demo.c

4.2 【指令】 -o

  指令-o(小写)用来指定生成的文件名。

Order o.png

结果如下:

Demo exe.png

  生成的文件不一定要在.c所在目录,可以给出路径指定:

cmd
1
gcc Demo.c -o ..\Demo.exe

  该指令指定将exe生成到上一级目录。
  生成exe后我们试者执行一下看看结果。

Run.png

4.3 【指令】 -E(预处理(Preprocessing))

  指令 -E(大写)将执行预处理操作也即生成*.i文件,gcc编译器将对#开头的指令进行解析。我们修改Demo.c中的代码为如下:

c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <stdlib.h>
#include "Test.h"

int main()
{
int a = N; //宏常量
int b = 2;
int c = 0;

c = a + b;
printf("%d\n", c);

CODE //宏替换代码段

DoNothing();

system("pause");
return 0;
}

  在Demo.c同目录下编辑Test.h文件,源码如下:

c
1
2
3
4
5
6
7
8
#define N 1

#define CODE if(c > 2) \
{ \
printf("c > 2\n"); \
}

void DoNothing(void); //函数声明(该函数未被调用)

  在Demo.c同目录下编辑Test.c文件,源码如下:

c
1
2
3
4
5
6
7
8
#include"Test.h"

void DoNothing(void)
{
;

return ;
}

  从源码可知Demo.c中在预处理阶段需要把调用的头文件包含进来,替换宏常量和宏代码段。我们执行指令。

cmd
1
gcc -E Demo.c -o Demo.i

  并看看Demo.i文件中的代码,Demo.i文件内容如下。

Order E.png

  -E 指令使gcc执行预处理,预处理时对#类指令进行处理(包含头文件、替换宏常量和宏代码段等操作),也就不难理解为何Demo.i文件会很大了(38KB),整个工程最后被揉合成一个大文件。预处理把注释都去掉了,所以反编译回来的代码是没有注释的!因为注释早就被去掉了。另外可以一提的是使用指令gcc -E Demo.c不指定输出的文件名时内容将会直接输出到Dos框中,而不会产生文件。
  我们知道c语言出现语法错误时编译器将会报错,但检查语法错误是在预处理、编译、汇编、链接、生成可执行文件中的哪个阶段执行的呢?为了方便我们编辑Demo2.c代码,有意使源码代码由于错误测试预处理阶段是否会报语法错误。

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

int main(void)
{
aabbccdd //此处有语法错误

return 0;
}

  对Demo2.c执行预处理操作gcc -E Demo2.c

Preprocessing Error.png

  由此可知预处理阶段不检查语法错误。

4.4 【指令】 -S(编译(Compiling))

  执行-S(大写)指令将*.i文件中源码转化为汇编代码*.s文件。我们执行指令gcc -S Demo.i -o Demo.s,在当前目录下将生成Demo.s文件,用记事本打开会发现c源码已经被编译器转化为汇编代码。

Assembly.png

  如果使用指令gcc -S Demo.i即不指定输出文件名,默认也将会在当前目录下产生文件Demo.s。
  关于前面含有语法错误的Demo2.c我们在编译阶段再试一次,看看是否能检查出语法错误。

Compiling Error.png

  对语法的检查是再编译阶段进行的。

4.5 【指令】 -c(汇编(Assembling))

  执行-c(小写)指令将*.s文件中的汇编源码转化未机器能执行的二进制机器码,生成文件*.o。执行指令gcc -c Demo.s -o Demo.o。生成的Demo.o为二进制文件,用记事本打开就是一堆乱码,我这里就不贴图出来了。

4.6 【指令】 gcc *.o(链接(Linking))

  经过汇编处理后生成的二进制文件Demo.o虽然已经机器码,但仍然无法运行因为少了链接操作。链接操作可执行指令gcc Demo.o -o Demo.exe

Linking Error.png

  对Demo.o执行链接操作出现报错,提示找不到函数DoNothing的定义。预处理阶段值时将头文件包含进Demo.i文件中,也即Demo.i中含有函数的声明部分,所以编译阶段只检查函数的声明和调用处是否符合函数原型,并未去检查其他*.c文件中的函数定义。
  链接阶段就是要把函数库中的函数定义给关联进来,找不到函数定义当然会报错。那么问题来了,我们调用的标准库函数printf为何不报错?其实库函数也不例外,在链接阶段需要关联到函数定义,它的声明部分在标准库函数头文件stdio.h中已给出,而实现部分在某个标准库中封装好了,不同的系统下还会有差异。gcc在链接时默认是包含标准库的库文件路径的,所以它能找到printf函数的定义而不会报错。而DoNothing函数是我们用户自己编写,并未封装成库的形式gcc当然找不到它的定义。倘若把Demo.c中的DoNothing函数调用去掉可以成功执行链接操作吗?当然可以!
  为了让Demo.o能成功的生成可执行文件Demo.exe,我们可以将Test.c封装成静态库供其调用,这样就有函数定义了。执行指令gcc -c Test.c -o Test.o生成Test.o二进制文件。这里并不是不用对Test.c执行预处理和编译操作,而是编译器帮我们做了。接着将Test.o生成静态库文件libTest.a,然后再次尝试对Demo.o执行链接操作。

lib Test.png

  可以看到这次编译器没有报错且成功生成了Demo.exe,执行结果也与预期一直。
  关于生成静态库和动态库的gcc指令以及更多其他指令,静态库和动态库的区别和作用,等有时间了继续写个中级篇来讲解吧,一篇文章写得太长大家看了也会枯燥,我写着也需要休息。

结果如下:

浅显易懂的GCC使用教程

前言:
   2018-12-17天气暖,属于冬日里出太阳。最近在学习使用gvim,想着抛弃对IDE的依赖同时也是想了解编译的过程,但除了学习gvim繁多的指令外还得先学习使用gcc编译程序。这篇文章将会用浅显易懂的方式记录下gcc的基本使用,同时也不忘扩展探究。

  • 什么是GCC,它能干什么?
  • GCC、gcc、g++三者有何关系?
  • 开始开发前该做什么准备?
  • gcc常用指令讲解?

1 什么是gcc,它能干什么?

  GCC(GNU Compiler Collection)即GNU编译器套件,属于一种编程语言编译器,其原名为GCC(GNU C Compiler)即GNU c语言编译器,虽然缩写一样但是功能上区别很大。GCC的初衷是为GNU操作系统专门编写的一款编译器,原本的GNU是专用于编译C代码,现如今已扩展为可以编译C、C++、Java、Objective-C等多种编程语言的编译器集合了。这篇文章主要介绍gcc或g++的使用。

2 GCC、gcc、g++三者有何关系?

  gcc(GUN C Compiler)是GCC中的c编译器,而g++(GUN C++ Compiler)是GCC中的c++编译器。
  gcc和g++两者都可以编译c和cpp文件,但存在差异。gcc在编译cpp时语法按照c来编译但默认不能链接到c++的库(gcc默认链接c库,g++默认链接c++库)。g++编译.c和.cpp文件都统一按cpp的语法规则来编译。所以一般编译c用gcc,编译c++用g++。后文有时间会继续深入探讨区别。

3 开始开发前该做什么准备?

3.1 软件安装教程

  直接去官网上下载MinGW的包管理器。
  下载好后安装包管理器并运行它,可以看到如下界面:

Package Manager.png

  左侧选择“Basic Setup”选项,右侧勾选(点击“Mark for Installation”)需要安装的组件“mingw32-gcc-g++”。假设你也需要使用MinGW编译Fortran、Object-C、Ada语言的话也可以勾上相应的选项。选择好后点击左上角的Installation菜单中的Apply changes选项即开始在线下载安装相关组件。
  安装好后最好配置下环境变量,把MinGw的bin目录加入到环境变量的path中,这样dos在任意目录下都可以调用bin目录下的可执行文件,其他一些软件也能找到gcc的位置。cmd中输入gcc -v查看版本号。

Version.png

4 gcc常用指令讲解?

  用gcc在Windows上编译*.c文件并非直接生成*.exe文件(Linux上为*.out),中间还经历了预处理、编译和汇编几个过程,好在gcc提供了生成中间文件的指令,虽然平时开发的时候很少关注编译过程中生成的这些*.i和*.s文件,但对其有一定的了解总归是好的。

Process.png

4.1 gcc指令

  使用gcc指令编译*.c文件可以生成*exe可执行文件。下面我们用一段简单的c代码来讲解,Demo.c内源码如下:

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

int main(void)
{
printf("nihao\n");

return 0;
}

  打开cmd,需要注意的是cmd默认路径一般是c盘,而Demo.c文件可能并不在cmd的默认路径下,我们需要使用dos指令cd到Demo.c所在目录下才能对文件进行操作。

Order gcc.png

  对Demo.c文件执行gcc指令后会在该目录下生成可执行文件a.exe,由于没有制定exe的文件名所以默认为a.exe。

a exe.png

  当然也不一定要cd进Demo.c的目录,使用gcc指令时给出Demo.c的绝对路径也可以成功编译,命令如下:

cmd
1
gcc E:\WorkSpace\Test_gcc\Demo.c

4.2 【指令】 -o

  指令-o(小写)用来指定生成的文件名。

Order o.png

结果如下:

Demo exe.png

  生成的文件不一定要在.c所在目录,可以给出路径指定:

cmd
1
gcc Demo.c -o ..\Demo.exe

  该指令指定将exe生成到上一级目录。
  生成exe后我们试者执行一下看看结果。

Run.png

4.3 【指令】 -E(预处理(Preprocessing))

  指令 -E(大写)将执行预处理操作也即生成*.i文件,gcc编译器将对#开头的指令进行解析。我们修改Demo.c中的代码为如下:

c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <stdlib.h>
#include "Test.h"

int main()
{
int a = N; //宏常量
int b = 2;
int c = 0;

c = a + b;
printf("%d\n", c);

CODE //宏替换代码段

DoNothing();

system("pause");
return 0;
}

  在Demo.c同目录下编辑Test.h文件,源码如下:

c
1
2
3
4
5
6
7
8
#define N 1

#define CODE if(c > 2) \
{ \
printf("c > 2\n"); \
}

void DoNothing(void); //函数声明(该函数未被调用)

  在Demo.c同目录下编辑Test.c文件,源码如下:

c
1
2
3
4
5
6
7
8
#include"Test.h"

void DoNothing(void)
{
;

return ;
}

  从源码可知Demo.c中在预处理阶段需要把调用的头文件包含进来,替换宏常量和宏代码段。我们执行指令。

cmd
1
gcc -E Demo.c -o Demo.i

  并看看Demo.i文件中的代码,Demo.i文件内容如下。

Order E.png

  -E 指令使gcc执行预处理,预处理时对#类指令进行处理(包含头文件、替换宏常量和宏代码段等操作),也就不难理解为何Demo.i文件会很大了(38KB),整个工程最后被揉合成一个大文件。预处理把注释都去掉了,所以反编译回来的代码是没有注释的!因为注释早就被去掉了。另外可以一提的是使用指令gcc -E Demo.c不指定输出的文件名时内容将会直接输出到Dos框中,而不会产生文件。
  我们知道c语言出现语法错误时编译器将会报错,但检查语法错误是在预处理、编译、汇编、链接、生成可执行文件中的哪个阶段执行的呢?为了方便我们编辑Demo2.c代码,有意使源码代码由于错误测试预处理阶段是否会报语法错误。

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

int main(void)
{
aabbccdd //此处有语法错误

return 0;
}

  对Demo2.c执行预处理操作gcc -E Demo2.c

Preprocessing Error.png

  由此可知预处理阶段不检查语法错误。

4.4 【指令】 -S(编译(Compiling))

  执行-S(大写)指令将*.i文件中源码转化为汇编代码*.s文件。我们执行指令gcc -S Demo.i -o Demo.s,在当前目录下将生成Demo.s文件,用记事本打开会发现c源码已经被编译器转化为汇编代码。

Assembly.png

  如果使用指令gcc -S Demo.i即不指定输出文件名,默认也将会在当前目录下产生文件Demo.s。
  关于前面含有语法错误的Demo2.c我们在编译阶段再试一次,看看是否能检查出语法错误。

Compiling Error.png

  对语法的检查是再编译阶段进行的。

4.5 【指令】 -c(汇编(Assembling))

  执行-c(小写)指令将*.s文件中的汇编源码转化未机器能执行的二进制机器码,生成文件*.o。执行指令gcc -c Demo.s -o Demo.o。生成的Demo.o为二进制文件,用记事本打开就是一堆乱码,我这里就不贴图出来了。

4.6 【指令】 gcc *.o(链接(Linking))

  经过汇编处理后生成的二进制文件Demo.o虽然已经机器码,但仍然无法运行因为少了链接操作。链接操作可执行指令gcc Demo.o -o Demo.exe

Linking Error.png

  对Demo.o执行链接操作出现报错,提示找不到函数DoNothing的定义。预处理阶段值时将头文件包含进Demo.i文件中,也即Demo.i中含有函数的声明部分,所以编译阶段只检查函数的声明和调用处是否符合函数原型,并未去检查其他*.c文件中的函数定义。
  链接阶段就是要把函数库中的函数定义给关联进来,找不到函数定义当然会报错。那么问题来了,我们调用的标准库函数printf为何不报错?其实库函数也不例外,在链接阶段需要关联到函数定义,它的声明部分在标准库函数头文件stdio.h中已给出,而实现部分在某个标准库中封装好了,不同的系统下还会有差异。gcc在链接时默认是包含标准库的库文件路径的,所以它能找到printf函数的定义而不会报错。而DoNothing函数是我们用户自己编写,并未封装成库的形式gcc当然找不到它的定义。倘若把Demo.c中的DoNothing函数调用去掉可以成功执行链接操作吗?当然可以!
  为了让Demo.o能成功的生成可执行文件Demo.exe,我们可以将Test.c封装成静态库供其调用,这样就有函数定义了。执行指令gcc -c Test.c -o Test.o生成Test.o二进制文件。这里并不是不用对Test.c执行预处理和编译操作,而是编译器帮我们做了。接着将Test.o生成静态库文件libTest.a,然后再次尝试对Demo.o执行链接操作。

lib Test.png

  可以看到这次编译器没有报错且成功生成了Demo.exe,执行结果也与预期一直。
  关于生成静态库和动态库的gcc指令以及更多其他指令,静态库和动态库的区别和作用,等有时间了继续写个中级篇来讲解吧,一篇文章写得太长大家看了也会枯燥,我写着也需要休息。

  生成的文件不一定要在.c所在目录,可以给出路径指定:

cmd
1
gcc Demo.c -o ..\Demo.exe

  该指令指定将exe生成到上一级目录。
  生成exe后我们试者执行一下看看结果。

浅显易懂的GCC使用教程

前言:
   2018-12-17天气暖,属于冬日里出太阳。最近在学习使用gvim,想着抛弃对IDE的依赖同时也是想了解编译的过程,但除了学习gvim繁多的指令外还得先学习使用gcc编译程序。这篇文章将会用浅显易懂的方式记录下gcc的基本使用,同时也不忘扩展探究。

  • 什么是GCC,它能干什么?
  • GCC、gcc、g++三者有何关系?
  • 开始开发前该做什么准备?
  • gcc常用指令讲解?

1 什么是gcc,它能干什么?

  GCC(GNU Compiler Collection)即GNU编译器套件,属于一种编程语言编译器,其原名为GCC(GNU C Compiler)即GNU c语言编译器,虽然缩写一样但是功能上区别很大。GCC的初衷是为GNU操作系统专门编写的一款编译器,原本的GNU是专用于编译C代码,现如今已扩展为可以编译C、C++、Java、Objective-C等多种编程语言的编译器集合了。这篇文章主要介绍gcc或g++的使用。

2 GCC、gcc、g++三者有何关系?

  gcc(GUN C Compiler)是GCC中的c编译器,而g++(GUN C++ Compiler)是GCC中的c++编译器。
  gcc和g++两者都可以编译c和cpp文件,但存在差异。gcc在编译cpp时语法按照c来编译但默认不能链接到c++的库(gcc默认链接c库,g++默认链接c++库)。g++编译.c和.cpp文件都统一按cpp的语法规则来编译。所以一般编译c用gcc,编译c++用g++。后文有时间会继续深入探讨区别。

3 开始开发前该做什么准备?

3.1 软件安装教程

  直接去官网上下载MinGW的包管理器。
  下载好后安装包管理器并运行它,可以看到如下界面:

Package Manager.png

  左侧选择“Basic Setup”选项,右侧勾选(点击“Mark for Installation”)需要安装的组件“mingw32-gcc-g++”。假设你也需要使用MinGW编译Fortran、Object-C、Ada语言的话也可以勾上相应的选项。选择好后点击左上角的Installation菜单中的Apply changes选项即开始在线下载安装相关组件。
  安装好后最好配置下环境变量,把MinGw的bin目录加入到环境变量的path中,这样dos在任意目录下都可以调用bin目录下的可执行文件,其他一些软件也能找到gcc的位置。cmd中输入gcc -v查看版本号。

Version.png

4 gcc常用指令讲解?

  用gcc在Windows上编译*.c文件并非直接生成*.exe文件(Linux上为*.out),中间还经历了预处理、编译和汇编几个过程,好在gcc提供了生成中间文件的指令,虽然平时开发的时候很少关注编译过程中生成的这些*.i和*.s文件,但对其有一定的了解总归是好的。

Process.png

4.1 gcc指令

  使用gcc指令编译*.c文件可以生成*exe可执行文件。下面我们用一段简单的c代码来讲解,Demo.c内源码如下:

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

int main(void)
{
printf("nihao\n");

return 0;
}

  打开cmd,需要注意的是cmd默认路径一般是c盘,而Demo.c文件可能并不在cmd的默认路径下,我们需要使用dos指令cd到Demo.c所在目录下才能对文件进行操作。

Order gcc.png

  对Demo.c文件执行gcc指令后会在该目录下生成可执行文件a.exe,由于没有制定exe的文件名所以默认为a.exe。

a exe.png

  当然也不一定要cd进Demo.c的目录,使用gcc指令时给出Demo.c的绝对路径也可以成功编译,命令如下:

cmd
1
gcc E:\WorkSpace\Test_gcc\Demo.c

4.2 【指令】 -o

  指令-o(小写)用来指定生成的文件名。

Order o.png

结果如下:

Demo exe.png

  生成的文件不一定要在.c所在目录,可以给出路径指定:

cmd
1
gcc Demo.c -o ..\Demo.exe

  该指令指定将exe生成到上一级目录。
  生成exe后我们试者执行一下看看结果。

Run.png

4.3 【指令】 -E(预处理(Preprocessing))

  指令 -E(大写)将执行预处理操作也即生成*.i文件,gcc编译器将对#开头的指令进行解析。我们修改Demo.c中的代码为如下:

c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <stdlib.h>
#include "Test.h"

int main()
{
int a = N; //宏常量
int b = 2;
int c = 0;

c = a + b;
printf("%d\n", c);

CODE //宏替换代码段

DoNothing();

system("pause");
return 0;
}

  在Demo.c同目录下编辑Test.h文件,源码如下:

c
1
2
3
4
5
6
7
8
#define N 1

#define CODE if(c > 2) \
{ \
printf("c > 2\n"); \
}

void DoNothing(void); //函数声明(该函数未被调用)

  在Demo.c同目录下编辑Test.c文件,源码如下:

c
1
2
3
4
5
6
7
8
#include"Test.h"

void DoNothing(void)
{
;

return ;
}

  从源码可知Demo.c中在预处理阶段需要把调用的头文件包含进来,替换宏常量和宏代码段。我们执行指令。

cmd
1
gcc -E Demo.c -o Demo.i

  并看看Demo.i文件中的代码,Demo.i文件内容如下。

Order E.png

  -E 指令使gcc执行预处理,预处理时对#类指令进行处理(包含头文件、替换宏常量和宏代码段等操作),也就不难理解为何Demo.i文件会很大了(38KB),整个工程最后被揉合成一个大文件。预处理把注释都去掉了,所以反编译回来的代码是没有注释的!因为注释早就被去掉了。另外可以一提的是使用指令gcc -E Demo.c不指定输出的文件名时内容将会直接输出到Dos框中,而不会产生文件。
  我们知道c语言出现语法错误时编译器将会报错,但检查语法错误是在预处理、编译、汇编、链接、生成可执行文件中的哪个阶段执行的呢?为了方便我们编辑Demo2.c代码,有意使源码代码由于错误测试预处理阶段是否会报语法错误。

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

int main(void)
{
aabbccdd //此处有语法错误

return 0;
}

  对Demo2.c执行预处理操作gcc -E Demo2.c

Preprocessing Error.png

  由此可知预处理阶段不检查语法错误。

4.4 【指令】 -S(编译(Compiling))

  执行-S(大写)指令将*.i文件中源码转化为汇编代码*.s文件。我们执行指令gcc -S Demo.i -o Demo.s,在当前目录下将生成Demo.s文件,用记事本打开会发现c源码已经被编译器转化为汇编代码。

Assembly.png

  如果使用指令gcc -S Demo.i即不指定输出文件名,默认也将会在当前目录下产生文件Demo.s。
  关于前面含有语法错误的Demo2.c我们在编译阶段再试一次,看看是否能检查出语法错误。

Compiling Error.png

  对语法的检查是再编译阶段进行的。

4.5 【指令】 -c(汇编(Assembling))

  执行-c(小写)指令将*.s文件中的汇编源码转化未机器能执行的二进制机器码,生成文件*.o。执行指令gcc -c Demo.s -o Demo.o。生成的Demo.o为二进制文件,用记事本打开就是一堆乱码,我这里就不贴图出来了。

4.6 【指令】 gcc *.o(链接(Linking))

  经过汇编处理后生成的二进制文件Demo.o虽然已经机器码,但仍然无法运行因为少了链接操作。链接操作可执行指令gcc Demo.o -o Demo.exe

Linking Error.png

  对Demo.o执行链接操作出现报错,提示找不到函数DoNothing的定义。预处理阶段值时将头文件包含进Demo.i文件中,也即Demo.i中含有函数的声明部分,所以编译阶段只检查函数的声明和调用处是否符合函数原型,并未去检查其他*.c文件中的函数定义。
  链接阶段就是要把函数库中的函数定义给关联进来,找不到函数定义当然会报错。那么问题来了,我们调用的标准库函数printf为何不报错?其实库函数也不例外,在链接阶段需要关联到函数定义,它的声明部分在标准库函数头文件stdio.h中已给出,而实现部分在某个标准库中封装好了,不同的系统下还会有差异。gcc在链接时默认是包含标准库的库文件路径的,所以它能找到printf函数的定义而不会报错。而DoNothing函数是我们用户自己编写,并未封装成库的形式gcc当然找不到它的定义。倘若把Demo.c中的DoNothing函数调用去掉可以成功执行链接操作吗?当然可以!
  为了让Demo.o能成功的生成可执行文件Demo.exe,我们可以将Test.c封装成静态库供其调用,这样就有函数定义了。执行指令gcc -c Test.c -o Test.o生成Test.o二进制文件。这里并不是不用对Test.c执行预处理和编译操作,而是编译器帮我们做了。接着将Test.o生成静态库文件libTest.a,然后再次尝试对Demo.o执行链接操作。

lib Test.png

  可以看到这次编译器没有报错且成功生成了Demo.exe,执行结果也与预期一直。
  关于生成静态库和动态库的gcc指令以及更多其他指令,静态库和动态库的区别和作用,等有时间了继续写个中级篇来讲解吧,一篇文章写得太长大家看了也会枯燥,我写着也需要休息。

4.3 【指令】 -E(预处理(Preprocessing))

  指令 -E(大写)将执行预处理操作也即生成*.i文件,gcc编译器将对#开头的指令进行解析。我们修改Demo.c中的代码为如下:

c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <stdlib.h>
#include "Test.h"

int main()
{
int a = N; //宏常量
int b = 2;
int c = 0;

c = a + b;
printf("%d\n", c);

CODE //宏替换代码段

DoNothing();

system("pause");
return 0;
}

  在Demo.c同目录下编辑Test.h文件,源码如下:

c
1
2
3
4
5
6
7
8
#define N 1

#define CODE if(c > 2) \
{ \
printf("c > 2\n"); \
}

void DoNothing(void); //函数声明(该函数未被调用)

  在Demo.c同目录下编辑Test.c文件,源码如下:

c
1
2
3
4
5
6
7
8
#include"Test.h"

void DoNothing(void)
{
;

return ;
}

  从源码可知Demo.c中在预处理阶段需要把调用的头文件包含进来,替换宏常量和宏代码段。我们执行指令。

cmd
1
gcc -E Demo.c -o Demo.i

  并看看Demo.i文件中的代码,Demo.i文件内容如下。

浅显易懂的GCC使用教程

前言:
   2018-12-17天气暖,属于冬日里出太阳。最近在学习使用gvim,想着抛弃对IDE的依赖同时也是想了解编译的过程,但除了学习gvim繁多的指令外还得先学习使用gcc编译程序。这篇文章将会用浅显易懂的方式记录下gcc的基本使用,同时也不忘扩展探究。

  • 什么是GCC,它能干什么?
  • GCC、gcc、g++三者有何关系?
  • 开始开发前该做什么准备?
  • gcc常用指令讲解?

1 什么是gcc,它能干什么?

  GCC(GNU Compiler Collection)即GNU编译器套件,属于一种编程语言编译器,其原名为GCC(GNU C Compiler)即GNU c语言编译器,虽然缩写一样但是功能上区别很大。GCC的初衷是为GNU操作系统专门编写的一款编译器,原本的GNU是专用于编译C代码,现如今已扩展为可以编译C、C++、Java、Objective-C等多种编程语言的编译器集合了。这篇文章主要介绍gcc或g++的使用。

2 GCC、gcc、g++三者有何关系?

  gcc(GUN C Compiler)是GCC中的c编译器,而g++(GUN C++ Compiler)是GCC中的c++编译器。
  gcc和g++两者都可以编译c和cpp文件,但存在差异。gcc在编译cpp时语法按照c来编译但默认不能链接到c++的库(gcc默认链接c库,g++默认链接c++库)。g++编译.c和.cpp文件都统一按cpp的语法规则来编译。所以一般编译c用gcc,编译c++用g++。后文有时间会继续深入探讨区别。

3 开始开发前该做什么准备?

3.1 软件安装教程

  直接去官网上下载MinGW的包管理器。
  下载好后安装包管理器并运行它,可以看到如下界面:

Package Manager.png

  左侧选择“Basic Setup”选项,右侧勾选(点击“Mark for Installation”)需要安装的组件“mingw32-gcc-g++”。假设你也需要使用MinGW编译Fortran、Object-C、Ada语言的话也可以勾上相应的选项。选择好后点击左上角的Installation菜单中的Apply changes选项即开始在线下载安装相关组件。
  安装好后最好配置下环境变量,把MinGw的bin目录加入到环境变量的path中,这样dos在任意目录下都可以调用bin目录下的可执行文件,其他一些软件也能找到gcc的位置。cmd中输入gcc -v查看版本号。

Version.png

4 gcc常用指令讲解?

  用gcc在Windows上编译*.c文件并非直接生成*.exe文件(Linux上为*.out),中间还经历了预处理、编译和汇编几个过程,好在gcc提供了生成中间文件的指令,虽然平时开发的时候很少关注编译过程中生成的这些*.i和*.s文件,但对其有一定的了解总归是好的。

Process.png

4.1 gcc指令

  使用gcc指令编译*.c文件可以生成*exe可执行文件。下面我们用一段简单的c代码来讲解,Demo.c内源码如下:

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

int main(void)
{
printf("nihao\n");

return 0;
}

  打开cmd,需要注意的是cmd默认路径一般是c盘,而Demo.c文件可能并不在cmd的默认路径下,我们需要使用dos指令cd到Demo.c所在目录下才能对文件进行操作。

Order gcc.png

  对Demo.c文件执行gcc指令后会在该目录下生成可执行文件a.exe,由于没有制定exe的文件名所以默认为a.exe。

a exe.png

  当然也不一定要cd进Demo.c的目录,使用gcc指令时给出Demo.c的绝对路径也可以成功编译,命令如下:

cmd
1
gcc E:\WorkSpace\Test_gcc\Demo.c

4.2 【指令】 -o

  指令-o(小写)用来指定生成的文件名。

Order o.png

结果如下:

Demo exe.png

  生成的文件不一定要在.c所在目录,可以给出路径指定:

cmd
1
gcc Demo.c -o ..\Demo.exe

  该指令指定将exe生成到上一级目录。
  生成exe后我们试者执行一下看看结果。

Run.png

4.3 【指令】 -E(预处理(Preprocessing))

  指令 -E(大写)将执行预处理操作也即生成*.i文件,gcc编译器将对#开头的指令进行解析。我们修改Demo.c中的代码为如下:

c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <stdlib.h>
#include "Test.h"

int main()
{
int a = N; //宏常量
int b = 2;
int c = 0;

c = a + b;
printf("%d\n", c);

CODE //宏替换代码段

DoNothing();

system("pause");
return 0;
}

  在Demo.c同目录下编辑Test.h文件,源码如下:

c
1
2
3
4
5
6
7
8
#define N 1

#define CODE if(c > 2) \
{ \
printf("c > 2\n"); \
}

void DoNothing(void); //函数声明(该函数未被调用)

  在Demo.c同目录下编辑Test.c文件,源码如下:

c
1
2
3
4
5
6
7
8
#include"Test.h"

void DoNothing(void)
{
;

return ;
}

  从源码可知Demo.c中在预处理阶段需要把调用的头文件包含进来,替换宏常量和宏代码段。我们执行指令。

cmd
1
gcc -E Demo.c -o Demo.i

  并看看Demo.i文件中的代码,Demo.i文件内容如下。

Order E.png

  -E 指令使gcc执行预处理,预处理时对#类指令进行处理(包含头文件、替换宏常量和宏代码段等操作),也就不难理解为何Demo.i文件会很大了(38KB),整个工程最后被揉合成一个大文件。预处理把注释都去掉了,所以反编译回来的代码是没有注释的!因为注释早就被去掉了。另外可以一提的是使用指令gcc -E Demo.c不指定输出的文件名时内容将会直接输出到Dos框中,而不会产生文件。
  我们知道c语言出现语法错误时编译器将会报错,但检查语法错误是在预处理、编译、汇编、链接、生成可执行文件中的哪个阶段执行的呢?为了方便我们编辑Demo2.c代码,有意使源码代码由于错误测试预处理阶段是否会报语法错误。

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

int main(void)
{
aabbccdd //此处有语法错误

return 0;
}

  对Demo2.c执行预处理操作gcc -E Demo2.c

Preprocessing Error.png

  由此可知预处理阶段不检查语法错误。

4.4 【指令】 -S(编译(Compiling))

  执行-S(大写)指令将*.i文件中源码转化为汇编代码*.s文件。我们执行指令gcc -S Demo.i -o Demo.s,在当前目录下将生成Demo.s文件,用记事本打开会发现c源码已经被编译器转化为汇编代码。

Assembly.png

  如果使用指令gcc -S Demo.i即不指定输出文件名,默认也将会在当前目录下产生文件Demo.s。
  关于前面含有语法错误的Demo2.c我们在编译阶段再试一次,看看是否能检查出语法错误。

Compiling Error.png

  对语法的检查是再编译阶段进行的。

4.5 【指令】 -c(汇编(Assembling))

  执行-c(小写)指令将*.s文件中的汇编源码转化未机器能执行的二进制机器码,生成文件*.o。执行指令gcc -c Demo.s -o Demo.o。生成的Demo.o为二进制文件,用记事本打开就是一堆乱码,我这里就不贴图出来了。

4.6 【指令】 gcc *.o(链接(Linking))

  经过汇编处理后生成的二进制文件Demo.o虽然已经机器码,但仍然无法运行因为少了链接操作。链接操作可执行指令gcc Demo.o -o Demo.exe

Linking Error.png

  对Demo.o执行链接操作出现报错,提示找不到函数DoNothing的定义。预处理阶段值时将头文件包含进Demo.i文件中,也即Demo.i中含有函数的声明部分,所以编译阶段只检查函数的声明和调用处是否符合函数原型,并未去检查其他*.c文件中的函数定义。
  链接阶段就是要把函数库中的函数定义给关联进来,找不到函数定义当然会报错。那么问题来了,我们调用的标准库函数printf为何不报错?其实库函数也不例外,在链接阶段需要关联到函数定义,它的声明部分在标准库函数头文件stdio.h中已给出,而实现部分在某个标准库中封装好了,不同的系统下还会有差异。gcc在链接时默认是包含标准库的库文件路径的,所以它能找到printf函数的定义而不会报错。而DoNothing函数是我们用户自己编写,并未封装成库的形式gcc当然找不到它的定义。倘若把Demo.c中的DoNothing函数调用去掉可以成功执行链接操作吗?当然可以!
  为了让Demo.o能成功的生成可执行文件Demo.exe,我们可以将Test.c封装成静态库供其调用,这样就有函数定义了。执行指令gcc -c Test.c -o Test.o生成Test.o二进制文件。这里并不是不用对Test.c执行预处理和编译操作,而是编译器帮我们做了。接着将Test.o生成静态库文件libTest.a,然后再次尝试对Demo.o执行链接操作。

lib Test.png

  可以看到这次编译器没有报错且成功生成了Demo.exe,执行结果也与预期一直。
  关于生成静态库和动态库的gcc指令以及更多其他指令,静态库和动态库的区别和作用,等有时间了继续写个中级篇来讲解吧,一篇文章写得太长大家看了也会枯燥,我写着也需要休息。

  -E 指令使gcc执行预处理,预处理时对#类指令进行处理(包含头文件、替换宏常量和宏代码段等操作),也就不难理解为何Demo.i文件会很大了(38KB),整个工程最后被揉合成一个大文件。预处理把注释都去掉了,所以反编译回来的代码是没有注释的!因为注释早就被去掉了。另外可以一提的是使用指令gcc -E Demo.c不指定输出的文件名时内容将会直接输出到Dos框中,而不会产生文件。
  我们知道c语言出现语法错误时编译器将会报错,但检查语法错误是在预处理、编译、汇编、链接、生成可执行文件中的哪个阶段执行的呢?为了方便我们编辑Demo2.c代码,有意使源码代码由于错误测试预处理阶段是否会报语法错误。

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

int main(void)
{
aabbccdd //此处有语法错误

return 0;
}

  对Demo2.c执行预处理操作gcc -E Demo2.c

浅显易懂的GCC使用教程

前言:
   2018-12-17天气暖,属于冬日里出太阳。最近在学习使用gvim,想着抛弃对IDE的依赖同时也是想了解编译的过程,但除了学习gvim繁多的指令外还得先学习使用gcc编译程序。这篇文章将会用浅显易懂的方式记录下gcc的基本使用,同时也不忘扩展探究。

  • 什么是GCC,它能干什么?
  • GCC、gcc、g++三者有何关系?
  • 开始开发前该做什么准备?
  • gcc常用指令讲解?

1 什么是gcc,它能干什么?

  GCC(GNU Compiler Collection)即GNU编译器套件,属于一种编程语言编译器,其原名为GCC(GNU C Compiler)即GNU c语言编译器,虽然缩写一样但是功能上区别很大。GCC的初衷是为GNU操作系统专门编写的一款编译器,原本的GNU是专用于编译C代码,现如今已扩展为可以编译C、C++、Java、Objective-C等多种编程语言的编译器集合了。这篇文章主要介绍gcc或g++的使用。

2 GCC、gcc、g++三者有何关系?

  gcc(GUN C Compiler)是GCC中的c编译器,而g++(GUN C++ Compiler)是GCC中的c++编译器。
  gcc和g++两者都可以编译c和cpp文件,但存在差异。gcc在编译cpp时语法按照c来编译但默认不能链接到c++的库(gcc默认链接c库,g++默认链接c++库)。g++编译.c和.cpp文件都统一按cpp的语法规则来编译。所以一般编译c用gcc,编译c++用g++。后文有时间会继续深入探讨区别。

3 开始开发前该做什么准备?

3.1 软件安装教程

  直接去官网上下载MinGW的包管理器。
  下载好后安装包管理器并运行它,可以看到如下界面:

Package Manager.png

  左侧选择“Basic Setup”选项,右侧勾选(点击“Mark for Installation”)需要安装的组件“mingw32-gcc-g++”。假设你也需要使用MinGW编译Fortran、Object-C、Ada语言的话也可以勾上相应的选项。选择好后点击左上角的Installation菜单中的Apply changes选项即开始在线下载安装相关组件。
  安装好后最好配置下环境变量,把MinGw的bin目录加入到环境变量的path中,这样dos在任意目录下都可以调用bin目录下的可执行文件,其他一些软件也能找到gcc的位置。cmd中输入gcc -v查看版本号。

Version.png

4 gcc常用指令讲解?

  用gcc在Windows上编译*.c文件并非直接生成*.exe文件(Linux上为*.out),中间还经历了预处理、编译和汇编几个过程,好在gcc提供了生成中间文件的指令,虽然平时开发的时候很少关注编译过程中生成的这些*.i和*.s文件,但对其有一定的了解总归是好的。

Process.png

4.1 gcc指令

  使用gcc指令编译*.c文件可以生成*exe可执行文件。下面我们用一段简单的c代码来讲解,Demo.c内源码如下:

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

int main(void)
{
printf("nihao\n");

return 0;
}

  打开cmd,需要注意的是cmd默认路径一般是c盘,而Demo.c文件可能并不在cmd的默认路径下,我们需要使用dos指令cd到Demo.c所在目录下才能对文件进行操作。

Order gcc.png

  对Demo.c文件执行gcc指令后会在该目录下生成可执行文件a.exe,由于没有制定exe的文件名所以默认为a.exe。

a exe.png

  当然也不一定要cd进Demo.c的目录,使用gcc指令时给出Demo.c的绝对路径也可以成功编译,命令如下:

cmd
1
gcc E:\WorkSpace\Test_gcc\Demo.c

4.2 【指令】 -o

  指令-o(小写)用来指定生成的文件名。

Order o.png

结果如下:

Demo exe.png

  生成的文件不一定要在.c所在目录,可以给出路径指定:

cmd
1
gcc Demo.c -o ..\Demo.exe

  该指令指定将exe生成到上一级目录。
  生成exe后我们试者执行一下看看结果。

Run.png

4.3 【指令】 -E(预处理(Preprocessing))

  指令 -E(大写)将执行预处理操作也即生成*.i文件,gcc编译器将对#开头的指令进行解析。我们修改Demo.c中的代码为如下:

c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <stdlib.h>
#include "Test.h"

int main()
{
int a = N; //宏常量
int b = 2;
int c = 0;

c = a + b;
printf("%d\n", c);

CODE //宏替换代码段

DoNothing();

system("pause");
return 0;
}

  在Demo.c同目录下编辑Test.h文件,源码如下:

c
1
2
3
4
5
6
7
8
#define N 1

#define CODE if(c > 2) \
{ \
printf("c > 2\n"); \
}

void DoNothing(void); //函数声明(该函数未被调用)

  在Demo.c同目录下编辑Test.c文件,源码如下:

c
1
2
3
4
5
6
7
8
#include"Test.h"

void DoNothing(void)
{
;

return ;
}

  从源码可知Demo.c中在预处理阶段需要把调用的头文件包含进来,替换宏常量和宏代码段。我们执行指令。

cmd
1
gcc -E Demo.c -o Demo.i

  并看看Demo.i文件中的代码,Demo.i文件内容如下。

Order E.png

  -E 指令使gcc执行预处理,预处理时对#类指令进行处理(包含头文件、替换宏常量和宏代码段等操作),也就不难理解为何Demo.i文件会很大了(38KB),整个工程最后被揉合成一个大文件。预处理把注释都去掉了,所以反编译回来的代码是没有注释的!因为注释早就被去掉了。另外可以一提的是使用指令gcc -E Demo.c不指定输出的文件名时内容将会直接输出到Dos框中,而不会产生文件。
  我们知道c语言出现语法错误时编译器将会报错,但检查语法错误是在预处理、编译、汇编、链接、生成可执行文件中的哪个阶段执行的呢?为了方便我们编辑Demo2.c代码,有意使源码代码由于错误测试预处理阶段是否会报语法错误。

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

int main(void)
{
aabbccdd //此处有语法错误

return 0;
}

  对Demo2.c执行预处理操作gcc -E Demo2.c

Preprocessing Error.png

  由此可知预处理阶段不检查语法错误。

4.4 【指令】 -S(编译(Compiling))

  执行-S(大写)指令将*.i文件中源码转化为汇编代码*.s文件。我们执行指令gcc -S Demo.i -o Demo.s,在当前目录下将生成Demo.s文件,用记事本打开会发现c源码已经被编译器转化为汇编代码。

Assembly.png

  如果使用指令gcc -S Demo.i即不指定输出文件名,默认也将会在当前目录下产生文件Demo.s。
  关于前面含有语法错误的Demo2.c我们在编译阶段再试一次,看看是否能检查出语法错误。

Compiling Error.png

  对语法的检查是再编译阶段进行的。

4.5 【指令】 -c(汇编(Assembling))

  执行-c(小写)指令将*.s文件中的汇编源码转化未机器能执行的二进制机器码,生成文件*.o。执行指令gcc -c Demo.s -o Demo.o。生成的Demo.o为二进制文件,用记事本打开就是一堆乱码,我这里就不贴图出来了。

4.6 【指令】 gcc *.o(链接(Linking))

  经过汇编处理后生成的二进制文件Demo.o虽然已经机器码,但仍然无法运行因为少了链接操作。链接操作可执行指令gcc Demo.o -o Demo.exe

Linking Error.png

  对Demo.o执行链接操作出现报错,提示找不到函数DoNothing的定义。预处理阶段值时将头文件包含进Demo.i文件中,也即Demo.i中含有函数的声明部分,所以编译阶段只检查函数的声明和调用处是否符合函数原型,并未去检查其他*.c文件中的函数定义。
  链接阶段就是要把函数库中的函数定义给关联进来,找不到函数定义当然会报错。那么问题来了,我们调用的标准库函数printf为何不报错?其实库函数也不例外,在链接阶段需要关联到函数定义,它的声明部分在标准库函数头文件stdio.h中已给出,而实现部分在某个标准库中封装好了,不同的系统下还会有差异。gcc在链接时默认是包含标准库的库文件路径的,所以它能找到printf函数的定义而不会报错。而DoNothing函数是我们用户自己编写,并未封装成库的形式gcc当然找不到它的定义。倘若把Demo.c中的DoNothing函数调用去掉可以成功执行链接操作吗?当然可以!
  为了让Demo.o能成功的生成可执行文件Demo.exe,我们可以将Test.c封装成静态库供其调用,这样就有函数定义了。执行指令gcc -c Test.c -o Test.o生成Test.o二进制文件。这里并不是不用对Test.c执行预处理和编译操作,而是编译器帮我们做了。接着将Test.o生成静态库文件libTest.a,然后再次尝试对Demo.o执行链接操作。

lib Test.png

  可以看到这次编译器没有报错且成功生成了Demo.exe,执行结果也与预期一直。
  关于生成静态库和动态库的gcc指令以及更多其他指令,静态库和动态库的区别和作用,等有时间了继续写个中级篇来讲解吧,一篇文章写得太长大家看了也会枯燥,我写着也需要休息。

  由此可知预处理阶段不检查语法错误。

4.4 【指令】 -S(编译(Compiling))

  执行-S(大写)指令将*.i文件中源码转化为汇编代码*.s文件。我们执行指令gcc -S Demo.i -o Demo.s,在当前目录下将生成Demo.s文件,用记事本打开会发现c源码已经被编译器转化为汇编代码。

浅显易懂的GCC使用教程

前言:
   2018-12-17天气暖,属于冬日里出太阳。最近在学习使用gvim,想着抛弃对IDE的依赖同时也是想了解编译的过程,但除了学习gvim繁多的指令外还得先学习使用gcc编译程序。这篇文章将会用浅显易懂的方式记录下gcc的基本使用,同时也不忘扩展探究。

  • 什么是GCC,它能干什么?
  • GCC、gcc、g++三者有何关系?
  • 开始开发前该做什么准备?
  • gcc常用指令讲解?

1 什么是gcc,它能干什么?

  GCC(GNU Compiler Collection)即GNU编译器套件,属于一种编程语言编译器,其原名为GCC(GNU C Compiler)即GNU c语言编译器,虽然缩写一样但是功能上区别很大。GCC的初衷是为GNU操作系统专门编写的一款编译器,原本的GNU是专用于编译C代码,现如今已扩展为可以编译C、C++、Java、Objective-C等多种编程语言的编译器集合了。这篇文章主要介绍gcc或g++的使用。

2 GCC、gcc、g++三者有何关系?

  gcc(GUN C Compiler)是GCC中的c编译器,而g++(GUN C++ Compiler)是GCC中的c++编译器。
  gcc和g++两者都可以编译c和cpp文件,但存在差异。gcc在编译cpp时语法按照c来编译但默认不能链接到c++的库(gcc默认链接c库,g++默认链接c++库)。g++编译.c和.cpp文件都统一按cpp的语法规则来编译。所以一般编译c用gcc,编译c++用g++。后文有时间会继续深入探讨区别。

3 开始开发前该做什么准备?

3.1 软件安装教程

  直接去官网上下载MinGW的包管理器。
  下载好后安装包管理器并运行它,可以看到如下界面:

Package Manager.png

  左侧选择“Basic Setup”选项,右侧勾选(点击“Mark for Installation”)需要安装的组件“mingw32-gcc-g++”。假设你也需要使用MinGW编译Fortran、Object-C、Ada语言的话也可以勾上相应的选项。选择好后点击左上角的Installation菜单中的Apply changes选项即开始在线下载安装相关组件。
  安装好后最好配置下环境变量,把MinGw的bin目录加入到环境变量的path中,这样dos在任意目录下都可以调用bin目录下的可执行文件,其他一些软件也能找到gcc的位置。cmd中输入gcc -v查看版本号。

Version.png

4 gcc常用指令讲解?

  用gcc在Windows上编译*.c文件并非直接生成*.exe文件(Linux上为*.out),中间还经历了预处理、编译和汇编几个过程,好在gcc提供了生成中间文件的指令,虽然平时开发的时候很少关注编译过程中生成的这些*.i和*.s文件,但对其有一定的了解总归是好的。

Process.png

4.1 gcc指令

  使用gcc指令编译*.c文件可以生成*exe可执行文件。下面我们用一段简单的c代码来讲解,Demo.c内源码如下:

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

int main(void)
{
printf("nihao\n");

return 0;
}

  打开cmd,需要注意的是cmd默认路径一般是c盘,而Demo.c文件可能并不在cmd的默认路径下,我们需要使用dos指令cd到Demo.c所在目录下才能对文件进行操作。

Order gcc.png

  对Demo.c文件执行gcc指令后会在该目录下生成可执行文件a.exe,由于没有制定exe的文件名所以默认为a.exe。

a exe.png

  当然也不一定要cd进Demo.c的目录,使用gcc指令时给出Demo.c的绝对路径也可以成功编译,命令如下:

cmd
1
gcc E:\WorkSpace\Test_gcc\Demo.c

4.2 【指令】 -o

  指令-o(小写)用来指定生成的文件名。

Order o.png

结果如下:

Demo exe.png

  生成的文件不一定要在.c所在目录,可以给出路径指定:

cmd
1
gcc Demo.c -o ..\Demo.exe

  该指令指定将exe生成到上一级目录。
  生成exe后我们试者执行一下看看结果。

Run.png

4.3 【指令】 -E(预处理(Preprocessing))

  指令 -E(大写)将执行预处理操作也即生成*.i文件,gcc编译器将对#开头的指令进行解析。我们修改Demo.c中的代码为如下:

c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <stdlib.h>
#include "Test.h"

int main()
{
int a = N; //宏常量
int b = 2;
int c = 0;

c = a + b;
printf("%d\n", c);

CODE //宏替换代码段

DoNothing();

system("pause");
return 0;
}

  在Demo.c同目录下编辑Test.h文件,源码如下:

c
1
2
3
4
5
6
7
8
#define N 1

#define CODE if(c > 2) \
{ \
printf("c > 2\n"); \
}

void DoNothing(void); //函数声明(该函数未被调用)

  在Demo.c同目录下编辑Test.c文件,源码如下:

c
1
2
3
4
5
6
7
8
#include"Test.h"

void DoNothing(void)
{
;

return ;
}

  从源码可知Demo.c中在预处理阶段需要把调用的头文件包含进来,替换宏常量和宏代码段。我们执行指令。

cmd
1
gcc -E Demo.c -o Demo.i

  并看看Demo.i文件中的代码,Demo.i文件内容如下。

Order E.png

  -E 指令使gcc执行预处理,预处理时对#类指令进行处理(包含头文件、替换宏常量和宏代码段等操作),也就不难理解为何Demo.i文件会很大了(38KB),整个工程最后被揉合成一个大文件。预处理把注释都去掉了,所以反编译回来的代码是没有注释的!因为注释早就被去掉了。另外可以一提的是使用指令gcc -E Demo.c不指定输出的文件名时内容将会直接输出到Dos框中,而不会产生文件。
  我们知道c语言出现语法错误时编译器将会报错,但检查语法错误是在预处理、编译、汇编、链接、生成可执行文件中的哪个阶段执行的呢?为了方便我们编辑Demo2.c代码,有意使源码代码由于错误测试预处理阶段是否会报语法错误。

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

int main(void)
{
aabbccdd //此处有语法错误

return 0;
}

  对Demo2.c执行预处理操作gcc -E Demo2.c

Preprocessing Error.png

  由此可知预处理阶段不检查语法错误。

4.4 【指令】 -S(编译(Compiling))

  执行-S(大写)指令将*.i文件中源码转化为汇编代码*.s文件。我们执行指令gcc -S Demo.i -o Demo.s,在当前目录下将生成Demo.s文件,用记事本打开会发现c源码已经被编译器转化为汇编代码。

Assembly.png

  如果使用指令gcc -S Demo.i即不指定输出文件名,默认也将会在当前目录下产生文件Demo.s。
  关于前面含有语法错误的Demo2.c我们在编译阶段再试一次,看看是否能检查出语法错误。

Compiling Error.png

  对语法的检查是再编译阶段进行的。

4.5 【指令】 -c(汇编(Assembling))

  执行-c(小写)指令将*.s文件中的汇编源码转化未机器能执行的二进制机器码,生成文件*.o。执行指令gcc -c Demo.s -o Demo.o。生成的Demo.o为二进制文件,用记事本打开就是一堆乱码,我这里就不贴图出来了。

4.6 【指令】 gcc *.o(链接(Linking))

  经过汇编处理后生成的二进制文件Demo.o虽然已经机器码,但仍然无法运行因为少了链接操作。链接操作可执行指令gcc Demo.o -o Demo.exe

Linking Error.png

  对Demo.o执行链接操作出现报错,提示找不到函数DoNothing的定义。预处理阶段值时将头文件包含进Demo.i文件中,也即Demo.i中含有函数的声明部分,所以编译阶段只检查函数的声明和调用处是否符合函数原型,并未去检查其他*.c文件中的函数定义。
  链接阶段就是要把函数库中的函数定义给关联进来,找不到函数定义当然会报错。那么问题来了,我们调用的标准库函数printf为何不报错?其实库函数也不例外,在链接阶段需要关联到函数定义,它的声明部分在标准库函数头文件stdio.h中已给出,而实现部分在某个标准库中封装好了,不同的系统下还会有差异。gcc在链接时默认是包含标准库的库文件路径的,所以它能找到printf函数的定义而不会报错。而DoNothing函数是我们用户自己编写,并未封装成库的形式gcc当然找不到它的定义。倘若把Demo.c中的DoNothing函数调用去掉可以成功执行链接操作吗?当然可以!
  为了让Demo.o能成功的生成可执行文件Demo.exe,我们可以将Test.c封装成静态库供其调用,这样就有函数定义了。执行指令gcc -c Test.c -o Test.o生成Test.o二进制文件。这里并不是不用对Test.c执行预处理和编译操作,而是编译器帮我们做了。接着将Test.o生成静态库文件libTest.a,然后再次尝试对Demo.o执行链接操作。

lib Test.png

  可以看到这次编译器没有报错且成功生成了Demo.exe,执行结果也与预期一直。
  关于生成静态库和动态库的gcc指令以及更多其他指令,静态库和动态库的区别和作用,等有时间了继续写个中级篇来讲解吧,一篇文章写得太长大家看了也会枯燥,我写着也需要休息。

  如果使用指令gcc -S Demo.i即不指定输出文件名,默认也将会在当前目录下产生文件Demo.s。
  关于前面含有语法错误的Demo2.c我们在编译阶段再试一次,看看是否能检查出语法错误。

浅显易懂的GCC使用教程

前言:
   2018-12-17天气暖,属于冬日里出太阳。最近在学习使用gvim,想着抛弃对IDE的依赖同时也是想了解编译的过程,但除了学习gvim繁多的指令外还得先学习使用gcc编译程序。这篇文章将会用浅显易懂的方式记录下gcc的基本使用,同时也不忘扩展探究。

  • 什么是GCC,它能干什么?
  • GCC、gcc、g++三者有何关系?
  • 开始开发前该做什么准备?
  • gcc常用指令讲解?

1 什么是gcc,它能干什么?

  GCC(GNU Compiler Collection)即GNU编译器套件,属于一种编程语言编译器,其原名为GCC(GNU C Compiler)即GNU c语言编译器,虽然缩写一样但是功能上区别很大。GCC的初衷是为GNU操作系统专门编写的一款编译器,原本的GNU是专用于编译C代码,现如今已扩展为可以编译C、C++、Java、Objective-C等多种编程语言的编译器集合了。这篇文章主要介绍gcc或g++的使用。

2 GCC、gcc、g++三者有何关系?

  gcc(GUN C Compiler)是GCC中的c编译器,而g++(GUN C++ Compiler)是GCC中的c++编译器。
  gcc和g++两者都可以编译c和cpp文件,但存在差异。gcc在编译cpp时语法按照c来编译但默认不能链接到c++的库(gcc默认链接c库,g++默认链接c++库)。g++编译.c和.cpp文件都统一按cpp的语法规则来编译。所以一般编译c用gcc,编译c++用g++。后文有时间会继续深入探讨区别。

3 开始开发前该做什么准备?

3.1 软件安装教程

  直接去官网上下载MinGW的包管理器。
  下载好后安装包管理器并运行它,可以看到如下界面:

Package Manager.png

  左侧选择“Basic Setup”选项,右侧勾选(点击“Mark for Installation”)需要安装的组件“mingw32-gcc-g++”。假设你也需要使用MinGW编译Fortran、Object-C、Ada语言的话也可以勾上相应的选项。选择好后点击左上角的Installation菜单中的Apply changes选项即开始在线下载安装相关组件。
  安装好后最好配置下环境变量,把MinGw的bin目录加入到环境变量的path中,这样dos在任意目录下都可以调用bin目录下的可执行文件,其他一些软件也能找到gcc的位置。cmd中输入gcc -v查看版本号。

Version.png

4 gcc常用指令讲解?

  用gcc在Windows上编译*.c文件并非直接生成*.exe文件(Linux上为*.out),中间还经历了预处理、编译和汇编几个过程,好在gcc提供了生成中间文件的指令,虽然平时开发的时候很少关注编译过程中生成的这些*.i和*.s文件,但对其有一定的了解总归是好的。

Process.png

4.1 gcc指令

  使用gcc指令编译*.c文件可以生成*exe可执行文件。下面我们用一段简单的c代码来讲解,Demo.c内源码如下:

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

int main(void)
{
printf("nihao\n");

return 0;
}

  打开cmd,需要注意的是cmd默认路径一般是c盘,而Demo.c文件可能并不在cmd的默认路径下,我们需要使用dos指令cd到Demo.c所在目录下才能对文件进行操作。

Order gcc.png

  对Demo.c文件执行gcc指令后会在该目录下生成可执行文件a.exe,由于没有制定exe的文件名所以默认为a.exe。

a exe.png

  当然也不一定要cd进Demo.c的目录,使用gcc指令时给出Demo.c的绝对路径也可以成功编译,命令如下:

cmd
1
gcc E:\WorkSpace\Test_gcc\Demo.c

4.2 【指令】 -o

  指令-o(小写)用来指定生成的文件名。

Order o.png

结果如下:

Demo exe.png

  生成的文件不一定要在.c所在目录,可以给出路径指定:

cmd
1
gcc Demo.c -o ..\Demo.exe

  该指令指定将exe生成到上一级目录。
  生成exe后我们试者执行一下看看结果。

Run.png

4.3 【指令】 -E(预处理(Preprocessing))

  指令 -E(大写)将执行预处理操作也即生成*.i文件,gcc编译器将对#开头的指令进行解析。我们修改Demo.c中的代码为如下:

c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <stdlib.h>
#include "Test.h"

int main()
{
int a = N; //宏常量
int b = 2;
int c = 0;

c = a + b;
printf("%d\n", c);

CODE //宏替换代码段

DoNothing();

system("pause");
return 0;
}

  在Demo.c同目录下编辑Test.h文件,源码如下:

c
1
2
3
4
5
6
7
8
#define N 1

#define CODE if(c > 2) \
{ \
printf("c > 2\n"); \
}

void DoNothing(void); //函数声明(该函数未被调用)

  在Demo.c同目录下编辑Test.c文件,源码如下:

c
1
2
3
4
5
6
7
8
#include"Test.h"

void DoNothing(void)
{
;

return ;
}

  从源码可知Demo.c中在预处理阶段需要把调用的头文件包含进来,替换宏常量和宏代码段。我们执行指令。

cmd
1
gcc -E Demo.c -o Demo.i

  并看看Demo.i文件中的代码,Demo.i文件内容如下。

Order E.png

  -E 指令使gcc执行预处理,预处理时对#类指令进行处理(包含头文件、替换宏常量和宏代码段等操作),也就不难理解为何Demo.i文件会很大了(38KB),整个工程最后被揉合成一个大文件。预处理把注释都去掉了,所以反编译回来的代码是没有注释的!因为注释早就被去掉了。另外可以一提的是使用指令gcc -E Demo.c不指定输出的文件名时内容将会直接输出到Dos框中,而不会产生文件。
  我们知道c语言出现语法错误时编译器将会报错,但检查语法错误是在预处理、编译、汇编、链接、生成可执行文件中的哪个阶段执行的呢?为了方便我们编辑Demo2.c代码,有意使源码代码由于错误测试预处理阶段是否会报语法错误。

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

int main(void)
{
aabbccdd //此处有语法错误

return 0;
}

  对Demo2.c执行预处理操作gcc -E Demo2.c

Preprocessing Error.png

  由此可知预处理阶段不检查语法错误。

4.4 【指令】 -S(编译(Compiling))

  执行-S(大写)指令将*.i文件中源码转化为汇编代码*.s文件。我们执行指令gcc -S Demo.i -o Demo.s,在当前目录下将生成Demo.s文件,用记事本打开会发现c源码已经被编译器转化为汇编代码。

Assembly.png

  如果使用指令gcc -S Demo.i即不指定输出文件名,默认也将会在当前目录下产生文件Demo.s。
  关于前面含有语法错误的Demo2.c我们在编译阶段再试一次,看看是否能检查出语法错误。

Compiling Error.png

  对语法的检查是再编译阶段进行的。

4.5 【指令】 -c(汇编(Assembling))

  执行-c(小写)指令将*.s文件中的汇编源码转化未机器能执行的二进制机器码,生成文件*.o。执行指令gcc -c Demo.s -o Demo.o。生成的Demo.o为二进制文件,用记事本打开就是一堆乱码,我这里就不贴图出来了。

4.6 【指令】 gcc *.o(链接(Linking))

  经过汇编处理后生成的二进制文件Demo.o虽然已经机器码,但仍然无法运行因为少了链接操作。链接操作可执行指令gcc Demo.o -o Demo.exe

Linking Error.png

  对Demo.o执行链接操作出现报错,提示找不到函数DoNothing的定义。预处理阶段值时将头文件包含进Demo.i文件中,也即Demo.i中含有函数的声明部分,所以编译阶段只检查函数的声明和调用处是否符合函数原型,并未去检查其他*.c文件中的函数定义。
  链接阶段就是要把函数库中的函数定义给关联进来,找不到函数定义当然会报错。那么问题来了,我们调用的标准库函数printf为何不报错?其实库函数也不例外,在链接阶段需要关联到函数定义,它的声明部分在标准库函数头文件stdio.h中已给出,而实现部分在某个标准库中封装好了,不同的系统下还会有差异。gcc在链接时默认是包含标准库的库文件路径的,所以它能找到printf函数的定义而不会报错。而DoNothing函数是我们用户自己编写,并未封装成库的形式gcc当然找不到它的定义。倘若把Demo.c中的DoNothing函数调用去掉可以成功执行链接操作吗?当然可以!
  为了让Demo.o能成功的生成可执行文件Demo.exe,我们可以将Test.c封装成静态库供其调用,这样就有函数定义了。执行指令gcc -c Test.c -o Test.o生成Test.o二进制文件。这里并不是不用对Test.c执行预处理和编译操作,而是编译器帮我们做了。接着将Test.o生成静态库文件libTest.a,然后再次尝试对Demo.o执行链接操作。

lib Test.png

  可以看到这次编译器没有报错且成功生成了Demo.exe,执行结果也与预期一直。
  关于生成静态库和动态库的gcc指令以及更多其他指令,静态库和动态库的区别和作用,等有时间了继续写个中级篇来讲解吧,一篇文章写得太长大家看了也会枯燥,我写着也需要休息。

  对语法的检查是再编译阶段进行的。

4.5 【指令】 -c(汇编(Assembling))

  执行-c(小写)指令将*.s文件中的汇编源码转化未机器能执行的二进制机器码,生成文件*.o。执行指令gcc -c Demo.s -o Demo.o。生成的Demo.o为二进制文件,用记事本打开就是一堆乱码,我这里就不贴图出来了。

4.6 【指令】 gcc *.o(链接(Linking))

  经过汇编处理后生成的二进制文件Demo.o虽然已经机器码,但仍然无法运行因为少了链接操作。链接操作可执行指令gcc Demo.o -o Demo.exe

浅显易懂的GCC使用教程

前言:
   2018-12-17天气暖,属于冬日里出太阳。最近在学习使用gvim,想着抛弃对IDE的依赖同时也是想了解编译的过程,但除了学习gvim繁多的指令外还得先学习使用gcc编译程序。这篇文章将会用浅显易懂的方式记录下gcc的基本使用,同时也不忘扩展探究。

  • 什么是GCC,它能干什么?
  • GCC、gcc、g++三者有何关系?
  • 开始开发前该做什么准备?
  • gcc常用指令讲解?

1 什么是gcc,它能干什么?

  GCC(GNU Compiler Collection)即GNU编译器套件,属于一种编程语言编译器,其原名为GCC(GNU C Compiler)即GNU c语言编译器,虽然缩写一样但是功能上区别很大。GCC的初衷是为GNU操作系统专门编写的一款编译器,原本的GNU是专用于编译C代码,现如今已扩展为可以编译C、C++、Java、Objective-C等多种编程语言的编译器集合了。这篇文章主要介绍gcc或g++的使用。

2 GCC、gcc、g++三者有何关系?

  gcc(GUN C Compiler)是GCC中的c编译器,而g++(GUN C++ Compiler)是GCC中的c++编译器。
  gcc和g++两者都可以编译c和cpp文件,但存在差异。gcc在编译cpp时语法按照c来编译但默认不能链接到c++的库(gcc默认链接c库,g++默认链接c++库)。g++编译.c和.cpp文件都统一按cpp的语法规则来编译。所以一般编译c用gcc,编译c++用g++。后文有时间会继续深入探讨区别。

3 开始开发前该做什么准备?

3.1 软件安装教程

  直接去官网上下载MinGW的包管理器。
  下载好后安装包管理器并运行它,可以看到如下界面:

Package Manager.png

  左侧选择“Basic Setup”选项,右侧勾选(点击“Mark for Installation”)需要安装的组件“mingw32-gcc-g++”。假设你也需要使用MinGW编译Fortran、Object-C、Ada语言的话也可以勾上相应的选项。选择好后点击左上角的Installation菜单中的Apply changes选项即开始在线下载安装相关组件。
  安装好后最好配置下环境变量,把MinGw的bin目录加入到环境变量的path中,这样dos在任意目录下都可以调用bin目录下的可执行文件,其他一些软件也能找到gcc的位置。cmd中输入gcc -v查看版本号。

Version.png

4 gcc常用指令讲解?

  用gcc在Windows上编译*.c文件并非直接生成*.exe文件(Linux上为*.out),中间还经历了预处理、编译和汇编几个过程,好在gcc提供了生成中间文件的指令,虽然平时开发的时候很少关注编译过程中生成的这些*.i和*.s文件,但对其有一定的了解总归是好的。

Process.png

4.1 gcc指令

  使用gcc指令编译*.c文件可以生成*exe可执行文件。下面我们用一段简单的c代码来讲解,Demo.c内源码如下:

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

int main(void)
{
printf("nihao\n");

return 0;
}

  打开cmd,需要注意的是cmd默认路径一般是c盘,而Demo.c文件可能并不在cmd的默认路径下,我们需要使用dos指令cd到Demo.c所在目录下才能对文件进行操作。

Order gcc.png

  对Demo.c文件执行gcc指令后会在该目录下生成可执行文件a.exe,由于没有制定exe的文件名所以默认为a.exe。

a exe.png

  当然也不一定要cd进Demo.c的目录,使用gcc指令时给出Demo.c的绝对路径也可以成功编译,命令如下:

cmd
1
gcc E:\WorkSpace\Test_gcc\Demo.c

4.2 【指令】 -o

  指令-o(小写)用来指定生成的文件名。

Order o.png

结果如下:

Demo exe.png

  生成的文件不一定要在.c所在目录,可以给出路径指定:

cmd
1
gcc Demo.c -o ..\Demo.exe

  该指令指定将exe生成到上一级目录。
  生成exe后我们试者执行一下看看结果。

Run.png

4.3 【指令】 -E(预处理(Preprocessing))

  指令 -E(大写)将执行预处理操作也即生成*.i文件,gcc编译器将对#开头的指令进行解析。我们修改Demo.c中的代码为如下:

c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <stdlib.h>
#include "Test.h"

int main()
{
int a = N; //宏常量
int b = 2;
int c = 0;

c = a + b;
printf("%d\n", c);

CODE //宏替换代码段

DoNothing();

system("pause");
return 0;
}

  在Demo.c同目录下编辑Test.h文件,源码如下:

c
1
2
3
4
5
6
7
8
#define N 1

#define CODE if(c > 2) \
{ \
printf("c > 2\n"); \
}

void DoNothing(void); //函数声明(该函数未被调用)

  在Demo.c同目录下编辑Test.c文件,源码如下:

c
1
2
3
4
5
6
7
8
#include"Test.h"

void DoNothing(void)
{
;

return ;
}

  从源码可知Demo.c中在预处理阶段需要把调用的头文件包含进来,替换宏常量和宏代码段。我们执行指令。

cmd
1
gcc -E Demo.c -o Demo.i

  并看看Demo.i文件中的代码,Demo.i文件内容如下。

Order E.png

  -E 指令使gcc执行预处理,预处理时对#类指令进行处理(包含头文件、替换宏常量和宏代码段等操作),也就不难理解为何Demo.i文件会很大了(38KB),整个工程最后被揉合成一个大文件。预处理把注释都去掉了,所以反编译回来的代码是没有注释的!因为注释早就被去掉了。另外可以一提的是使用指令gcc -E Demo.c不指定输出的文件名时内容将会直接输出到Dos框中,而不会产生文件。
  我们知道c语言出现语法错误时编译器将会报错,但检查语法错误是在预处理、编译、汇编、链接、生成可执行文件中的哪个阶段执行的呢?为了方便我们编辑Demo2.c代码,有意使源码代码由于错误测试预处理阶段是否会报语法错误。

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

int main(void)
{
aabbccdd //此处有语法错误

return 0;
}

  对Demo2.c执行预处理操作gcc -E Demo2.c

Preprocessing Error.png

  由此可知预处理阶段不检查语法错误。

4.4 【指令】 -S(编译(Compiling))

  执行-S(大写)指令将*.i文件中源码转化为汇编代码*.s文件。我们执行指令gcc -S Demo.i -o Demo.s,在当前目录下将生成Demo.s文件,用记事本打开会发现c源码已经被编译器转化为汇编代码。

Assembly.png

  如果使用指令gcc -S Demo.i即不指定输出文件名,默认也将会在当前目录下产生文件Demo.s。
  关于前面含有语法错误的Demo2.c我们在编译阶段再试一次,看看是否能检查出语法错误。

Compiling Error.png

  对语法的检查是再编译阶段进行的。

4.5 【指令】 -c(汇编(Assembling))

  执行-c(小写)指令将*.s文件中的汇编源码转化未机器能执行的二进制机器码,生成文件*.o。执行指令gcc -c Demo.s -o Demo.o。生成的Demo.o为二进制文件,用记事本打开就是一堆乱码,我这里就不贴图出来了。

4.6 【指令】 gcc *.o(链接(Linking))

  经过汇编处理后生成的二进制文件Demo.o虽然已经机器码,但仍然无法运行因为少了链接操作。链接操作可执行指令gcc Demo.o -o Demo.exe

Linking Error.png

  对Demo.o执行链接操作出现报错,提示找不到函数DoNothing的定义。预处理阶段值时将头文件包含进Demo.i文件中,也即Demo.i中含有函数的声明部分,所以编译阶段只检查函数的声明和调用处是否符合函数原型,并未去检查其他*.c文件中的函数定义。
  链接阶段就是要把函数库中的函数定义给关联进来,找不到函数定义当然会报错。那么问题来了,我们调用的标准库函数printf为何不报错?其实库函数也不例外,在链接阶段需要关联到函数定义,它的声明部分在标准库函数头文件stdio.h中已给出,而实现部分在某个标准库中封装好了,不同的系统下还会有差异。gcc在链接时默认是包含标准库的库文件路径的,所以它能找到printf函数的定义而不会报错。而DoNothing函数是我们用户自己编写,并未封装成库的形式gcc当然找不到它的定义。倘若把Demo.c中的DoNothing函数调用去掉可以成功执行链接操作吗?当然可以!
  为了让Demo.o能成功的生成可执行文件Demo.exe,我们可以将Test.c封装成静态库供其调用,这样就有函数定义了。执行指令gcc -c Test.c -o Test.o生成Test.o二进制文件。这里并不是不用对Test.c执行预处理和编译操作,而是编译器帮我们做了。接着将Test.o生成静态库文件libTest.a,然后再次尝试对Demo.o执行链接操作。

lib Test.png

  可以看到这次编译器没有报错且成功生成了Demo.exe,执行结果也与预期一直。
  关于生成静态库和动态库的gcc指令以及更多其他指令,静态库和动态库的区别和作用,等有时间了继续写个中级篇来讲解吧,一篇文章写得太长大家看了也会枯燥,我写着也需要休息。

  对Demo.o执行链接操作出现报错,提示找不到函数DoNothing的定义。预处理阶段值时将头文件包含进Demo.i文件中,也即Demo.i中含有函数的声明部分,所以编译阶段只检查函数的声明和调用处是否符合函数原型,并未去检查其他*.c文件中的函数定义。
  链接阶段就是要把函数库中的函数定义给关联进来,找不到函数定义当然会报错。那么问题来了,我们调用的标准库函数printf为何不报错?其实库函数也不例外,在链接阶段需要关联到函数定义,它的声明部分在标准库函数头文件stdio.h中已给出,而实现部分在某个标准库中封装好了,不同的系统下还会有差异。gcc在链接时默认是包含标准库的库文件路径的,所以它能找到printf函数的定义而不会报错。而DoNothing函数是我们用户自己编写,并未封装成库的形式gcc当然找不到它的定义。倘若把Demo.c中的DoNothing函数调用去掉可以成功执行链接操作吗?当然可以!
  为了让Demo.o能成功的生成可执行文件Demo.exe,我们可以将Test.c封装成静态库供其调用,这样就有函数定义了。执行指令gcc -c Test.c -o Test.o生成Test.o二进制文件。这里并不是不用对Test.c执行预处理和编译操作,而是编译器帮我们做了。接着将Test.o生成静态库文件libTest.a,然后再次尝试对Demo.o执行链接操作。

浅显易懂的GCC使用教程

前言:
   2018-12-17天气暖,属于冬日里出太阳。最近在学习使用gvim,想着抛弃对IDE的依赖同时也是想了解编译的过程,但除了学习gvim繁多的指令外还得先学习使用gcc编译程序。这篇文章将会用浅显易懂的方式记录下gcc的基本使用,同时也不忘扩展探究。

  • 什么是GCC,它能干什么?
  • GCC、gcc、g++三者有何关系?
  • 开始开发前该做什么准备?
  • gcc常用指令讲解?

1 什么是gcc,它能干什么?

  GCC(GNU Compiler Collection)即GNU编译器套件,属于一种编程语言编译器,其原名为GCC(GNU C Compiler)即GNU c语言编译器,虽然缩写一样但是功能上区别很大。GCC的初衷是为GNU操作系统专门编写的一款编译器,原本的GNU是专用于编译C代码,现如今已扩展为可以编译C、C++、Java、Objective-C等多种编程语言的编译器集合了。这篇文章主要介绍gcc或g++的使用。

2 GCC、gcc、g++三者有何关系?

  gcc(GUN C Compiler)是GCC中的c编译器,而g++(GUN C++ Compiler)是GCC中的c++编译器。
  gcc和g++两者都可以编译c和cpp文件,但存在差异。gcc在编译cpp时语法按照c来编译但默认不能链接到c++的库(gcc默认链接c库,g++默认链接c++库)。g++编译.c和.cpp文件都统一按cpp的语法规则来编译。所以一般编译c用gcc,编译c++用g++。后文有时间会继续深入探讨区别。

3 开始开发前该做什么准备?

3.1 软件安装教程

  直接去官网上下载MinGW的包管理器。
  下载好后安装包管理器并运行它,可以看到如下界面:

Package Manager.png

  左侧选择“Basic Setup”选项,右侧勾选(点击“Mark for Installation”)需要安装的组件“mingw32-gcc-g++”。假设你也需要使用MinGW编译Fortran、Object-C、Ada语言的话也可以勾上相应的选项。选择好后点击左上角的Installation菜单中的Apply changes选项即开始在线下载安装相关组件。
  安装好后最好配置下环境变量,把MinGw的bin目录加入到环境变量的path中,这样dos在任意目录下都可以调用bin目录下的可执行文件,其他一些软件也能找到gcc的位置。cmd中输入gcc -v查看版本号。

Version.png

4 gcc常用指令讲解?

  用gcc在Windows上编译*.c文件并非直接生成*.exe文件(Linux上为*.out),中间还经历了预处理、编译和汇编几个过程,好在gcc提供了生成中间文件的指令,虽然平时开发的时候很少关注编译过程中生成的这些*.i和*.s文件,但对其有一定的了解总归是好的。

Process.png

4.1 gcc指令

  使用gcc指令编译*.c文件可以生成*exe可执行文件。下面我们用一段简单的c代码来讲解,Demo.c内源码如下:

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

int main(void)
{
printf("nihao\n");

return 0;
}

  打开cmd,需要注意的是cmd默认路径一般是c盘,而Demo.c文件可能并不在cmd的默认路径下,我们需要使用dos指令cd到Demo.c所在目录下才能对文件进行操作。

Order gcc.png

  对Demo.c文件执行gcc指令后会在该目录下生成可执行文件a.exe,由于没有制定exe的文件名所以默认为a.exe。

a exe.png

  当然也不一定要cd进Demo.c的目录,使用gcc指令时给出Demo.c的绝对路径也可以成功编译,命令如下:

cmd
1
gcc E:\WorkSpace\Test_gcc\Demo.c

4.2 【指令】 -o

  指令-o(小写)用来指定生成的文件名。

Order o.png

结果如下:

Demo exe.png

  生成的文件不一定要在.c所在目录,可以给出路径指定:

cmd
1
gcc Demo.c -o ..\Demo.exe

  该指令指定将exe生成到上一级目录。
  生成exe后我们试者执行一下看看结果。

Run.png

4.3 【指令】 -E(预处理(Preprocessing))

  指令 -E(大写)将执行预处理操作也即生成*.i文件,gcc编译器将对#开头的指令进行解析。我们修改Demo.c中的代码为如下:

c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <stdlib.h>
#include "Test.h"

int main()
{
int a = N; //宏常量
int b = 2;
int c = 0;

c = a + b;
printf("%d\n", c);

CODE //宏替换代码段

DoNothing();

system("pause");
return 0;
}

  在Demo.c同目录下编辑Test.h文件,源码如下:

c
1
2
3
4
5
6
7
8
#define N 1

#define CODE if(c > 2) \
{ \
printf("c > 2\n"); \
}

void DoNothing(void); //函数声明(该函数未被调用)

  在Demo.c同目录下编辑Test.c文件,源码如下:

c
1
2
3
4
5
6
7
8
#include"Test.h"

void DoNothing(void)
{
;

return ;
}

  从源码可知Demo.c中在预处理阶段需要把调用的头文件包含进来,替换宏常量和宏代码段。我们执行指令。

cmd
1
gcc -E Demo.c -o Demo.i

  并看看Demo.i文件中的代码,Demo.i文件内容如下。

Order E.png

  -E 指令使gcc执行预处理,预处理时对#类指令进行处理(包含头文件、替换宏常量和宏代码段等操作),也就不难理解为何Demo.i文件会很大了(38KB),整个工程最后被揉合成一个大文件。预处理把注释都去掉了,所以反编译回来的代码是没有注释的!因为注释早就被去掉了。另外可以一提的是使用指令gcc -E Demo.c不指定输出的文件名时内容将会直接输出到Dos框中,而不会产生文件。
  我们知道c语言出现语法错误时编译器将会报错,但检查语法错误是在预处理、编译、汇编、链接、生成可执行文件中的哪个阶段执行的呢?为了方便我们编辑Demo2.c代码,有意使源码代码由于错误测试预处理阶段是否会报语法错误。

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

int main(void)
{
aabbccdd //此处有语法错误

return 0;
}

  对Demo2.c执行预处理操作gcc -E Demo2.c

Preprocessing Error.png

  由此可知预处理阶段不检查语法错误。

4.4 【指令】 -S(编译(Compiling))

  执行-S(大写)指令将*.i文件中源码转化为汇编代码*.s文件。我们执行指令gcc -S Demo.i -o Demo.s,在当前目录下将生成Demo.s文件,用记事本打开会发现c源码已经被编译器转化为汇编代码。

Assembly.png

  如果使用指令gcc -S Demo.i即不指定输出文件名,默认也将会在当前目录下产生文件Demo.s。
  关于前面含有语法错误的Demo2.c我们在编译阶段再试一次,看看是否能检查出语法错误。

Compiling Error.png

  对语法的检查是再编译阶段进行的。

4.5 【指令】 -c(汇编(Assembling))

  执行-c(小写)指令将*.s文件中的汇编源码转化未机器能执行的二进制机器码,生成文件*.o。执行指令gcc -c Demo.s -o Demo.o。生成的Demo.o为二进制文件,用记事本打开就是一堆乱码,我这里就不贴图出来了。

4.6 【指令】 gcc *.o(链接(Linking))

  经过汇编处理后生成的二进制文件Demo.o虽然已经机器码,但仍然无法运行因为少了链接操作。链接操作可执行指令gcc Demo.o -o Demo.exe

Linking Error.png

  对Demo.o执行链接操作出现报错,提示找不到函数DoNothing的定义。预处理阶段值时将头文件包含进Demo.i文件中,也即Demo.i中含有函数的声明部分,所以编译阶段只检查函数的声明和调用处是否符合函数原型,并未去检查其他*.c文件中的函数定义。
  链接阶段就是要把函数库中的函数定义给关联进来,找不到函数定义当然会报错。那么问题来了,我们调用的标准库函数printf为何不报错?其实库函数也不例外,在链接阶段需要关联到函数定义,它的声明部分在标准库函数头文件stdio.h中已给出,而实现部分在某个标准库中封装好了,不同的系统下还会有差异。gcc在链接时默认是包含标准库的库文件路径的,所以它能找到printf函数的定义而不会报错。而DoNothing函数是我们用户自己编写,并未封装成库的形式gcc当然找不到它的定义。倘若把Demo.c中的DoNothing函数调用去掉可以成功执行链接操作吗?当然可以!
  为了让Demo.o能成功的生成可执行文件Demo.exe,我们可以将Test.c封装成静态库供其调用,这样就有函数定义了。执行指令gcc -c Test.c -o Test.o生成Test.o二进制文件。这里并不是不用对Test.c执行预处理和编译操作,而是编译器帮我们做了。接着将Test.o生成静态库文件libTest.a,然后再次尝试对Demo.o执行链接操作。

lib Test.png

  可以看到这次编译器没有报错且成功生成了Demo.exe,执行结果也与预期一直。
  关于生成静态库和动态库的gcc指令以及更多其他指令,静态库和动态库的区别和作用,等有时间了继续写个中级篇来讲解吧,一篇文章写得太长大家看了也会枯燥,我写着也需要休息。

  可以看到这次编译器没有报错且成功生成了Demo.exe,执行结果也与预期一直。
  关于生成静态库和动态库的gcc指令以及更多其他指令,静态库和动态库的区别和作用,等有时间了继续写个中级篇来讲解吧,一篇文章写得太长大家看了也会枯燥,我写着也需要休息。