浅显易懂的GCC使用教程——初级篇

浅显易懂的GCC使用教程——初级篇

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

——Author:Calm

  • 什么是GCC,它能干什么?

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

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

  • gcc常用指令讲解?

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

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

  • 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++。后文有时间会继续深入探讨区别。

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

    • 软件安装教程:
        直接去官网上下载MinGW的包管理器(下载链接:https://osdn.net/projects/mingw/releases/)

        下载好后安装包管理器并运行它,可以看到如下界面:

      浅显易懂的GCC使用教程——初级篇_第1张图片

        左侧选择“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使用教程——初级篇_第2张图片

  • gcc常用指令讲解?

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

    在这里插入图片描述

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

      
      	#include
      	
      	int main(void)
      	{
      	    printf("nihao\n");
      	
      	    return 0;
      	}
      
      

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

      浅显易懂的GCC使用教程——初级篇_第3张图片

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

      alt text

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

      gcc E:\WorkSpace\Test_gcc\Demo.c

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

      alt text

      结果如下:

      alt text

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

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

        该指令指定将exe生成到上一级目录。

        生成exe后我们试者执行一下看看结果。

      alt text

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

      
      	#include 
      	#include 
      	#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文件,源码如下:

      
      	#define N 1
      	
      	#define     CODE      if(c > 2)                              \
      	                                {                            \
      	                                    printf("c > 2\n");       \
      	                                }
      	
      	void DoNothing(void);           //函数声明(该函数未被调用)
      
      

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

      
      	#include"Test.h"
      	
      	void DoNothing(void)
      	{
      	    ;
      	
      	    return ;
      	}
      
      

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

      Demo.i文件内容:

      浅显易懂的GCC使用教程——初级篇_第4张图片

        -E 指令使gcc执行预处理,预处理时对#类指令进行处理(包含头文件、替换宏常量和宏代码段等操作),也就不难理解为何Demo.i文件会很大了(38KB),整个工程最后被揉合成一个大文件。预处理把注释都去掉了,所以反编译回来的代码是没有注释的!因为注释早就被去掉了。另外可以一提的是使用指令gcc -E Demo.c不指定输出的文件名时内容将会直接输出到Dos框中,而不会产生文件。

        我们知道c语言出现语法错误时编译器将会报错,但检查语法错误是在预处理、编译、汇编、链接、生成可执行文件中的哪个阶段执行的呢?为了方便我们编辑Demo2.c代码,有意使源码代码由于错误测试预处理阶段是否会报语法错误。

      
      	#include
      	
      	int main(void)
      	{
      	    aabbccdd          //此处有语法错误
      	
      	    return 0;
      	}
      
      

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

      浅显易懂的GCC使用教程——初级篇_第5张图片

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

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

      浅显易懂的GCC使用教程——初级篇_第6张图片

        如果使用指令gcc -S Demo.i即不指定输出文件名,默认也将会在当前目录下产生文件Demo.s。

        关于前面含有语法错误的Demo2.c我们在编译阶段再试一次,看看是否能检查出语法错误。

      浅显易懂的GCC使用教程——初级篇_第7张图片

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

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

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

      浅显易懂的GCC使用教程——初级篇_第8张图片

        对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使用教程——初级篇_第9张图片

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

你可能感兴趣的:(GCC)