Linux下GCC教程

环境:Ubuntu 18.04.6

文章参考:爱编程的大丙 (subingwen.cn)

简介:

GCCLinux下的编译工具集,是GNU Compiler Collection的缩写,包含gccg++等编译器,该工具及不仅包含编译器,还包含其他工具集,例如arnum等。

GCC工具集不仅能编译C/C++语言,其它例如Objective-C、Pascal、FOrtan、Java、Ada等语言均能进行编译。GCC可以根据不同的硬件平台进行编译,可以进行交叉编译,在A平台上编译B平台上的程序,支持常见的X86、ARM、PowerPC、mips等,以及Linux、Windows等软件平台。

1. 安装

sudo apt update # 更新apt下载源,尽可能保证下载到的软件是最新的
sudo apt install gcc g++	# 同时下载gcc和g++

安装完毕后,查看版本:

gcc -v
g++ -v

2. 工作流程

GCC编译器的工作流程分为四步:

  1. 预处理:GCC调用预处理器主要完成三件事:
    • 展开头文件
    • 宏替换
    • 去掉注释行
  2. 编译和优化:GCC调用编译器对文件进行编译,得到汇编文件。
  3. 汇编:GCC调用汇编器对汇编文件进行汇编,得到二进制文件。
  4. 链接:GCC调用链接器对程序需要的库进行链接,最终得到一个可执行的二进制文件。

具体如下:

处理前文件名后缀 处理 gcc参数 处理后文件名后缀
.c/.cpp 预处理 -E .i
.i 编译 -S .s
.s 汇编 -c .o
.o 链接 无固定后缀

EG:

  1. 新建程序:test.c

    // 假设程序对应的源文件名为 test.c
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    
    int main()
    {
        #ifdef HELLO
        printf("HELLO宏被定义");
        #endif
        int arr[5] = {1,2,3,4,5};
        for(int i=0; i<5; ++i)
        {
            printf("arr[%d] = %d\n", i, array[i]);
        }
        return 0;
    }
    
  2. 预编译:

    gcc -E test.c -o test.i
    

    得到文件形式如下:

    // 前面是展开的头文件
    
    int main()
    {
        
        
        
        int arr[5] = {1,2,3,4,5};
        for(int i=0; i<5; ++i)
        {
            printf("arr[%d] = %d\n", i, array[i]);
        }
        return 0;
    }
    

    可以看出:

    • 头文件被展开
    • 注释被消去
    • 宏常量被替换。
  3. 编译:

    gcc -S test.i -o test.s
    

    得到的test.s文件内是汇编语言。

  4. 汇编:

    gcc -c test.s -o test.o
    

    得到二进制文件。

  5. 链接:

    gcc test.o -o test
    

    得到可执行文件test

  6. 执行test文件,输出如下:

    ./test
    arr[0] = 1
    arr[1] = 2
    arr[2] = 3
    arr[3] = 4
    arr[4] = 5
    

3. gcc常用参数

以下表格列出gcc常用参数,这些参数在实际使用中并无顺序之分,只要指定即可。

参数 作用
-E 对源文件预编译,不进行编译
-S 对源文件进行编译,不进行汇编。
-c 对源文件进行汇编,不进行链接。
-o file 将原文件编译为file
-I 指定include头文件的搜索路径
-D 在程序编译时,定义一个宏(在预编译阶段生效,因为该阶段会进行宏替换)
-w 不生成任何警告信息。(不推荐,因为有时警告信息就是报错)
-Wall 生成所有警告信息。
-O[n] 编译时优化代码。n代表优化的级别,0不优化,1为缺省值,3优化级别最高。
-l(小写的l) 在程序编译时,指定使用的库的名字。要去掉前缀和后缀
-L 指定编译的时候,搜索的库的路径。相对或绝对路径均可
-fpic/fPIC 生成与位置无关的代码(通常动态链接库使用)
-shared 生成共享目标文件。通常在建立共享库时。
-std 指定C/C++方言,如-std=c99,gcc默认的方亚是GNU C

3.1 搜索头文件(-I)

注意是大写的i不是小写的l

编写文件如下:

  1. 头文件./include/head.h:

    #ifndef _HEAD_H
    #define _HEAD_H
    // 加法
    int add(int a, int b);
    // 减法
    int sub(int a, int b);
    #endif
    
  2. 加法文件:./add.c

    #include <stdio.h>
    #include "head.h"
    int add(int a, int b){
        return a+b;
    }	
    
  3. 减法文件:./sub.c

    #include <stdio.h>
    #include "head.h"
    
    int sub(int a, int b)
    {
        return a-b;
    }
    
  4. 测试文件:./main.c:

    #include <stdio.h>
    #include "head.h"
    
    int main()
    {
        int a = 10;
        int b = 5;
        printf("a+b=%d\n", add(a, b));
        printf("a-b=%d\n", sub(a,b));
        return 0;
    }
    

最终文件结构如下:

.
├── add.c
├── cal
├── include
│   └── head.h
├── main.c
└── sub.c

此时使用gcc进行编译,发现无法找到头文件head.h。头文件寻找策略如下:

  • 在c/c++中,头文件如果使用<>,那就是从默认库中寻找(linux默认在/usr/lib
  • 如果是""包裹,那就从当前目录下寻找。

而头文件在include下,自然找不到,因此要在编译时指定头文件目录:

gcc *.c -o cal -I include/

编译成功。

3.2 定义宏(-D)

在编译程序时我们可以额外指定一个宏,这个宏会被写入文件中,从而对文件进行一定的控制。该指令实际在预编译阶段使用,因为该阶段会展开宏。

EG:

  1. 定义程序如下:

    #include <stdio.h>
    #define NUMBER 3
    
    int main()
    {
        #ifdef DEBUG
    	printf("这是一条调试信息\n");
        #endif
        for (int i = 0; i < NUMBER; i++)
        {
            printf("%d\n", i);
        }
        return 0;
    }
    
  2. 不指定宏进行编译:此时不会输出调试语句

    gcc test.c -o test.o
    ./test.o
    0
    1
    2
    
  3. 指定宏进行编译:

    gcc test.c -o test.o -D DEBUG
    ./test.o
    这是一条调试信息
    0
    1
    2
    

使用场景:

用于控制调试语句,将所有调试语句根据宏的存在与否判断是否编译,这样就可以在灵活控制程序中调试信息输出与否。

4. gcc与g++

二者之间的区别如下:

  • 在编译阶段(即预编译之后的阶段):
    • 后命名为.c的文件,gcc将其视作C程序,而g++将其视作C++程序。
    • 后缀名为.cpp的文件,gcc和g++都会将其视作C++程序。
    • 在该阶段g++会调用gcc对文件进行编译。也就是说编译阶段只会由gcc完成。
  • 在链接阶段(最后一个阶段):
    • gcc和g++都能连接到标准C库。
    • g++可以自动链接到C++标注库,而gcc想要连接到C++标准库需要参数lstdc++
  • 关于_cplusplus宏的定义
    • g++会自动定义该宏,但这并不影响其对于c文件的编译。
    • gcc需要根据后缀名来确认是否需要定义该宏。

综上:

  • gcc和g++都能编译C文件
  • g++能直接编译C++文件,而gcc需要添加参数-lstdc++
  • gcc和g++都能定义宏_cplusplus

EG:

# 编译c程序
gcc test.c -o test
g++ test.c -o test
# 编译c++程序
g++ test.cpp -o test
gcc test.cpp -o test -lstdc++
posted @   BinaryPrinter  阅读(36)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示