简介

「GCC」GNU Compiler Collection,GNU编译器套件

  1. 由GNU开发的语言编译器
  2. 原先只支持编译C语言。如今已成为了一个编译集,支持C、C++、Java等语言的编译

「gcc」小写的gcc,代表的是GCC编译集中C语言的编译器
「g++」GCC编译集中C++的编译器。如果不装g++,一些C++的库可能会没有,可能会编译不过

编译过程

1272518-20180609165653202-1098756463.png

  1. 编译过程: | 过程 | 说明 | 会出现的错误 | | —- | —- | —- | | 预处理(Preprocessing) | 生成.i文件 | 宏定义错误 | | 编译(Compilation) | 生成.s文件 | 代码错误,比如语法错误等 | | 汇编(Assembly) | 生成.o文件 | | | 链接(Linking) | 生成可执行文件 | 找不到资源、找不到库文件 |

  2. 说明:在linux的gcc中,可以对每个步骤分别做处理。而在Windows当中,一般只能控制到预处理、编译和链接部分,汇编一般只有相对硬件开发才会涉及到。

    语法

  3. gcc用于编译C语言的代码

  4. g++用于编译C++的代码

    -o(指定输出文件名,生成可执行文件)

    此文件名可以没有后缀(test),也可加后缀(test.out)。此输出文件即为可执行文件(类比windows的test.exe
    image.png

    -c(只编译不链接,生成.o文件)

    说明:一个项目若包含很多个文件时,经常会用到这个命令,只编译其中一个cpp,不链接其他的cpp
    1. gcc -c main.c
    2. //只编译main.c,不链接。生成了main.o文件
    3. gcc main.o -o main
    4. //再链接。生成了执行文件

    -E(预编译)

    预编译:把所有的头文件、宏全部拼到一个.c文件中
    1. gcc -E main.c>main.e
    2. //gcc -E main.c之后,在命令行打印出的内容很多,可以使用>把内容输出到文件main.e中
    3. vim main.e
    4. //打开文件,查看预编译的结果

    -S(只编译,不汇编)

    1. gcc -S main.c
    2. //只编译,不汇编。生成了一个main.s
    3. vim main.s
    4. //打开看一下结果,main.s即是汇编代码

    -g(生成调试信息)

    -g即表示生成Debug版本的可执行文件
    若没有-g,即默认情况(gcc main.c -o main)是release版本 ```shell gcc -g main.c -o main_d //gcc main.c -o main_d表示编译main.c,结果文件名为main_d(可执行文件) //-g表示这是一个有调试的版本
  1. <a name="ljLEQ"></a>
  2. # 编译
  3. <a name="y9m3H"></a>
  4. ## 多文件编译
  5. - src 文件目录
  6. - person
  7. - person.h
  8. - person.cpp
  9. - text.cpp 此文件中使用了person.h
  10. ```shell
  11. g++ main.cpp
  12. main.cpp:2:20: fatal error: Person.h: No such file or directory
  13. comilation terminated.
  14. //预编译出错。提示找不到Person.h,所以我们要指定头文件路径(-I),如果多个路径,再加-I../Person2
  15. g++ main.cpp -o main -I../Person
  16. /tmp/cct3XXsz.o: In function 'main':
  17. main.cpp:(.text+0x26): undefined reference to `Person::Person()`
  18. collect2: error: Id returned 1 exit status
  19. //编译出错。找不到Person的实现,即找不到.cpp文件
  20. g++ main.cpp ../Person/Person.cpp -o main -I../Person
  21. //指定Person.cpp,把它一起编译了

Linux静态编译

静态编译:静态编译会把所有的库都编译到一个文件中,这样把执行文件放到任何环境中都能运行,它并不要求运行环境。
动态编译:动态编译不把依赖库一起编译到可执行文件中。故动态编译会依赖很多库,很依赖于运行环境,如果环境中没有依赖库,就会运行失败。
例如:

  1. g++ main.cpp -o main //编译成动态的可执行文件
  2. ldd main //查看main的所有引用
  3. linux-vdso.so.1 => (....) //可以看到它引用了很多库
  4. g++ main.cpp -o main_static -static //编译成静态的可执行文件
  5. ldd main_static //查看引用
  6. not a dynamic executable //可以看到静态版本没有引用动态的可执行文件

综上:

  1. 不建议用静态的方式来编译
    • 一方面编译静态库的时间很长(跨平台项目本身编译的时间就很长,1分钟和10分钟的区别),另一方面静态库编译出来的可执行文件很大
    • 静态库带来的运行的简单,但编译非常麻烦。
  2. 当然有一些应用场景,为了运行时的简单,选择静态编译也可以的。

    Linux动态编译和调用

    动态编译:
    1. g++ Person.cpp -fpic -shared -o libPerson.so
    2. //-fpic表示.cpp中的函数与代码位置不相关-->可以通过头文件中的名字找到函数所在的位置
    3. //-shared表示这是一个动态库
    4. //-o指定生成文件的名称
    5. //文件名以lib开头,.so结尾,所以中间的Person才是这个库的名字,即此动态库最终文件名为Person.so
    调用:
    1. g++ main.cpp -o main -I../Person -L../Person -lPerson
    2. //-L指定动态链接库的路径,如果不指定默认到系统环境变量的lib路径下找
    3. //-l指定动态链接库的名称,即上面指定libPerson.so中间的Person
    执行:执行的时候会报错误,没有指定这个库的文件路径
    image.png
    两种解决方案:
  • 将.so拷贝到系统的path下面
  • 执行脚本的方式,在命令行输入export LD_LIBRARY_PATH=../Person

image.png