• Linux编译器-gcc/g++使用


    1. 背景知识

    1. 预处理(进行宏替换)
    2. 编译(生成汇编)
    3. 汇编(生成机器可识别代码)
    4. 链接(生成可执行文件或库文件)

    2. gcc如何完成

    格式 gcc [选项] 要编译的文件 [选项] [目标文件]

    1. [yyh@ecs-86081 lesson2]$ cat mytest.c
    2. #include
    3. #define NUM 100
    4. int main()
    5. {
    6. printf("hello world\n");
    7. //printf("hello world\n");
    8. //printf("hello world\n");
    9. //printf("hello world\n");
    10. //printf("hello world\n");
    11. printf("hello world\n");
    12. printf("NUM=%d\n",NUM);
    13. return 0;
    14. }
    15. [yyh@ecs-86081 lesson2]$ gcc mytest.c -o mytest
    16. [yyh@ecs-86081 lesson2]$ ll
    17. total 44
    18. -rwxrwxr-x 1 yyh yyh 8408 Jul 27 22:19 mytest
    19. -rw-rw-r-- 1 yyh yyh 298 Jul 27 21:53 mytest.c
    20. [yyh@ecs-86081 lesson2]$ ./mytest
    21. hello world
    22. hello world
    23. NUM=100

    预处理(进行宏替换)

    1. [yyh@ecs-86081 lesson2]$ gcc -E mytest.c -o mytest.i
    2. [yyh@ecs-86081 lesson2]$ ll
    3. total 24
    4. -rw-rw-r-- 1 yyh yyh 298 Jul 27 21:53 mytest.c
    5. -rw-rw-r-- 1 yyh yyh 16956 Jul 27 21:58 mytest.i

    ·预处理功能主要包括宏替换,文件包含,条件编译,去注释等。
    ·预处理指令是以#号开头的代码行。
    ·实例: gcc –E mytest.c –o mytest.i
    ·选项“-E”,该选项的作用是让 gcc 在预处理结束后停止编译过程。
    ·选项“-o”是指目标文件,“.i”文件为已经过预处理的C原始程序。

    编译(生成汇编)

    在这个阶段中,gcc 首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,gcc 把代码翻译成汇编语言。
    用户可以使用“-S”选项来进行查看,该选项只进行编译,生成汇编代码。
    实例: gcc –S mytest.i –o mytest.s

    1. [yyh@ecs-86081 lesson2]$ ll
    2. total 24
    3. -rw-rw-r-- 1 yyh yyh 298 Jul 27 21:53 mytest.c
    4. -rw-rw-r-- 1 yyh yyh 16956 Jul 27 22:01 mytest.i
    5. [yyh@ecs-86081 lesson2]$ gcc -S mytest.i -o mytest.s
    6. [yyh@ecs-86081 lesson2]$ ll
    7. total 28
    8. -rw-rw-r-- 1 yyh yyh 298 Jul 27 21:53 mytest.c
    9. -rw-rw-r-- 1 yyh yyh 16956 Jul 27 22:01 mytest.i
    10. -rw-rw-r-- 1 yyh yyh 567 Jul 27 22:04 mytest.s

    大家觉得编译器生成汇编就结束了吗?问:计算机可以直接执行汇编语言吗?汇编语言需要编译器吗?
    答:计算机不可以直接执行汇编语言!汇编语言也有编译器。计算机只认识二进制。

    汇编(生成机器可识别代码)

    汇编阶段是把编译阶段生成的“.s”文件转成目标文件
    可使用选项“-c”就可看到汇编代码已转化为“.o”的二进制目标代码了

    1. [yyh@ecs-86081 lesson2]$ ll
    2. total 28
    3. -rw-rw-r-- 1 yyh yyh 298 Jul 27 21:53 mytest.c
    4. -rw-rw-r-- 1 yyh yyh 16956 Jul 27 22:01 mytest.i
    5. -rw-rw-r-- 1 yyh yyh 567 Jul 27 22:04 mytest.s
    6. [yyh@ecs-86081 lesson2]$ gcc -c mytest.s -o mytest.o
    7. [yyh@ecs-86081 lesson2]$ ll
    8. total 32
    9. -rw-rw-r-- 1 yyh yyh 298 Jul 27 21:53 mytest.c
    10. -rw-rw-r-- 1 yyh yyh 16956 Jul 27 22:01 mytest.i
    11. -rw-rw-r-- 1 yyh yyh 1664 Jul 27 22:09 mytest.o
    12. -rw-rw-r-- 1 yyh yyh 567 Jul 27 22:04 mytest.s

    实例: gcc –c mytest.s –o mytest.o

    汇编形成的二进制文件,虽然是二进制文件,并不可以直接执行,该文件叫作可重定向目标文件.

    1. [yyh@ecs-86081 lesson2]$ ./mytest.o
    2. -bash: ./mytest.o: cannot execute binary file

    链接(生成可执行文件或库文件)

    在成功编译之后,就进入了链接阶段。为什么需要链接呢?因为我们要通过链接将自己的代码中的函数调用,外部数据和第三方提供库的方法关联起来。
    实例: gcc mytest.o –o mytest

    1. [yyh@ecs-86081 lesson2]$ ll
    2. total 32
    3. -rw-rw-r-- 1 yyh yyh 298 Jul 27 21:53 mytest.c
    4. -rw-rw-r-- 1 yyh yyh 16956 Jul 27 22:01 mytest.i
    5. -rwxrwxr-x 1 yyh yyh 1664 Jul 27 22:09 mytest.o
    6. -rw-rw-r-- 1 yyh yyh 567 Jul 27 22:04 mytest.s
    7. [yyh@ecs-86081 lesson2]$ gcc mytest.o -o mytest
    8. [yyh@ecs-86081 lesson2]$ ll
    9. total 44
    10. -rwxrwxr-x 1 yyh yyh 8408 Jul 27 22:17 mytest
    11. -rw-rw-r-- 1 yyh yyh 298 Jul 27 21:53 mytest.c
    12. -rw-rw-r-- 1 yyh yyh 16956 Jul 27 22:01 mytest.i
    13. -rwxrwxr-x 1 yyh yyh 1664 Jul 27 22:09 mytest.o
    14. -rw-rw-r-- 1 yyh yyh 567 Jul 27 22:04 mytest.s

    为什么C程序的翻译是上面这个过程??

    在这里涉及到一个重要的概念:函数库

    我们的C程序中,并没有定义“printf”的函数实现,且在预编译中包含的“stdio.h”中也只有该函数的声明,而没有定义函数的实现,那么,是在哪里实“printf”函数的呢?

    1. [yyh@ecs-86081 lesson2]$ ldd mytest
    2. linux-vdso.so.1 => (0x00007fff99753000)
    3. libc.so.6 => /lib64/libc.so.6 (0x00007fec21e81000)
    4. /lib64/ld-linux-x86-64.so.2 (0x00007fec2224f000)

    最后的答案是:系统把这些函数实现都弄到名为 libc.so.6 的库文件中去了,在没有特别指定时,gcc 会到系统默认的搜索路径“/usr/lib”下进行查找,也就是链接到 libc.so.6 库函数中去,这样就能实现函数“printf”了,而这也就是链接的作用。

    函数库一般分为静态库和动态库两种

    静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了。其后缀名一般为“.a”

    [yyh@ecs-86081 lesson2]$ gcc mytest.c -o mytest_static -static
    

    动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以节省系统的开销。动态库一般后缀名为“.so”,如前面所述的 libc.so.6 就是动态库。gcc 在编译时默认使用动态库。完成了链接之后,gcc 就可以生成可执行文件。
    gcc默认生成的二进制程序,是动态链接的,这点可以通过 file 命令验证。

    1. [yyh@ecs-86081 lesson2]$ file mytest
    2. mytest: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=65686cf353714574de3fe1b428ef53d056c43e15, not stripped

    gcc选项

    ·-E 只激活预处理,这个不生成文件,需要把它重定向到一个输出文件里面
    ·-S 编译到汇编语言不进行汇编和链接
    ·-c 编译到目标代码
    ·-o 文件输出到文件
    ·-static 此选项对生成的文件采用静态链接
    ·-g 生成调试信息。GNU 调试器可利用该信息。
    ·-shared 此选项将尽量使用动态库,所以生成文件比较小,但是需要系统由动态库.
    ·-O0
    ·-O1
    ·-O2
    ·-O3 编译器的优化选项的4个级别,-O0表示没有优化,-O1为缺省值,-O3优化级别最高
    ·-w 不生成任何警告信息。
    ·-Wall 生成所有警告信息。

    gcc选项记忆

    键盘左上角的esc选项对应生成iso

    g++使用样例

    1. [yyh@ecs-86081 lesson2]$ ll
    2. total 8
    3. -rw-rw-r-- 1 yyh yyh 298 Jul 27 21:53 mytest.c
    4. -rw-rw-r-- 1 yyh yyh 90 Jul 27 23:04 mytest.cpp
    5. [yyh@ecs-86081 lesson2]$ cat mytest.cpp
    6. #include
    7. int main()
    8. {
    9. std::cout<<"hello world"<
    10. return 0;
    11. }
    12. [yyh@ecs-86081 lesson2]$ g++ mytest.cpp -o mytest
    13. [yyh@ecs-86081 lesson2]$ ll
    14. total 20
    15. -rwxrwxr-x 1 yyh yyh 8968 Jul 27 23:04 mytest
    16. -rw-rw-r-- 1 yyh yyh 298 Jul 27 21:53 mytest.c
    17. -rw-rw-r-- 1 yyh yyh 90 Jul 27 23:04 mytest.cpp
    18. [yyh@ecs-86081 lesson2]$ ./mytest
    19. hello world

  • 相关阅读:
    C++中虚表是什么
    前端开发,自定义本地域名解析,更改host,模拟线上环境
    怎么将psd转化为jpg?收藏这几个方法
    百度地图根据地址获取经纬度
    区块链(9):java区块链项目的Web服务实现之实现web服务
    SSM工作流程
    C++(11):原子类型的is_lock_free/is_always_lock_free
    Overleaf论文排版踩坑记录
    Docker基础-3.本地镜像发布与容器数据卷
    【华为游戏服务】同一游戏同一个手机号的华为帐号登录返回的playerId不同
  • 原文地址:https://blog.csdn.net/weixin_56054625/article/details/126023792