【Linux】Linux环境下GCC编译

发布于:2022-07-24 ⋅ 阅读:(383) ⋅ 点赞:(0)

【Linux】Linux环境下GCC编译


前言

最初的GNU C编译器(GCC)是由Richard Stallman开发的,他是GNU项目的创始人。Richard Stallman在1984年创立了GNU项目,旨在创建一个完整的类unix操作系统作为自由软件,以促进计算机用户和程序员之间的自由和合作。

GCC,以前是“GNU C编译器”,随着时间的推移,已经支持许多语言,如C (GCC), c++ (g++), Objective-C, objective - c++, Java (gcj), Fortran (gfortran), Ada (gnat), Go (gccgo), OpenMP, Cilk Plus,和OpenAcc。它现在被称为“GNU编译器集合”。GCC的母站点是http://gcc.gnu.org/。当前版本是GCC 7.3,发布于2018-01-25。

GCC是所谓的“GNU工具链”的关键组件,用于开发应用程序和编写操作系统。GNU工具链包括:

  • GCC (GNU Compiler Collection):一个支持多种语言的编译器套件,例如C/ c++和Objective-C/ c++。
  • GNU Make:一个用于编译和构建应用程序的自动化工具。
  • GNU Binutils:一套二进制实用工具,包括链接器和汇编器。
  • GNU调试器(GDB)。
  • GNU Autotools:一个包含Autoconf, Autoheader, Automake和Libtool的构建系统。
  • GNU Bison:解析器生成器(类似于lex和yacc)。

GCC是可移植的,可以在许多操作平台上运行。GCC(和GNU Toolchain)目前在所有unix上都可用。它们也被移植到Windows(由Cygwin, MinGW和MinGW- w64)。GCC也是一个交叉编译器,用于在不同的平台上生成可执行文件。
GNU C和c++编译器分别称为gcc和g++

一、GCC编译过程

[vvvcxjvvv@localhost GCC_Test]$ cat hello.cpp
#include <iostream>
using namespace std;

int main(){
	cout << "Hello World!" << endl;
	cout << "Hello Linux!" << endl;
	return 0;
}
[vvvcxjvvv@localhost GCC_Test]$ ls
hello.cpp

1.1 一步编译

使用g++ 源文件可以直接对源文件进行预处理、编译、汇编、链接,生成可执行文件

[vvvcxjvvv@localhost GCC_Test]$ ls
hello.cpp
[vvvcxjvvv@localhost GCC_Test]$ g++ -o2 hello.cpp -o hello
[vvvcxjvvv@localhost GCC_Test]$ ls
hello  hello.cpp
[vvvcxjvvv@localhost GCC_Test]$ ./hello
Hello World!
Hello Linux!
[vvvcxjvvv@localhost GCC_Test]$ 

1.2 编译过程拆解

在这里插入图片描述

1.2.1 预处理Pre-processing

预处理阶段主要做以下六个任务:
(1)删除#define,并做文本替换;
(2)递归展开头文件;
(3)处理以#开头的预编译指令;
(4)删除注释部分;
(5)添加行号和文件标识;
(6)保留#pragma指令,供给编译器,
预编译阶段的语言还属于高级语言,计算机不能理解.

通过包含头文件(# include)和展开宏(# define)的GNU C/CPP预处理器生成包含扩展的源代码的中间文件(.i)

[vvvcxjvvv@localhost GCC_Test]$ ls
hello.cpp
[vvvcxjvvv@localhost GCC_Test]$ g++ -E hello.cpp -o hello.i
[vvvcxjvvv@localhost GCC_Test]$ ls
hello.cpp  hello.i

1.2.2 编译-Compiling

编译阶段主要进行以下五个任务:
(1)进行词法分析;
(2)语法分析;
(3)语义分析;
(4)代码优化;
(5)生成汇编指令
编译阶段的语言属于低级语言.

编译器将预处理的源代码编译为特定处理器的汇编代码(.s)

[vvvcxjvvv@localhost GCC_Test]$ g++ -S hello.i -o hello.s
[vvvcxjvvv@localhost GCC_Test]$ ls
hello.cpp  hello.i  hello.s

1.2.3 汇编-Assembling

汇编阶段主要是翻译指令(即将低级语言翻译成机器语言.

将汇编代码转换为目标文件中的机器代码(.o),这一步产生的文件叫做目标文件,是二进制格式。

[vvvcxjvvv@localhost GCC_Test]$ g++ -c hello.s -o hello.o
[vvvcxjvvv@localhost GCC_Test]$ ls
hello.cpp  hello.i  hello.o  hello.s

1.2.4 链接-Linking

链接阶段主要做汇编阶段未做的事情:
(1)强弱符号的处理;
(2)外部符号的处理;
(3)指令段中虚假地址和虚假偏移的处理;
(4)符号的重定位.

链接器将目标代码与库代码链接,以生成可执行文件

[vvvcxjvvv@localhost GCC_Test]$ g++ hello.o -o hello
[vvvcxjvvv@localhost GCC_Test]$ ls
hello  hello.cpp  hello.i  hello.o  hello.s
[vvvcxjvvv@localhost GCC_Test]$ ./hello
Hello World!
Hello Linux!
[vvvcxjvvv@localhost GCC_Test]$ 

二、重要编译参数

2.1 gcc/g++使用手册

man g++/ man gcc

GCC(1)                                GNU                               GCC(1)

NAME
       gcc - GNU project C and C++ compiler

SYNOPSIS
       gcc [-c|-S|-E] [-std=standard]
           [-g] [-pg] [-Olevel]
           [-Wwarn...] [-Wpedantic]
           [-Idir...] [-Ldir...]
           [-Dmacro[=defn]...] [-Umacro]
           [-foption...] [-mmachine-option...]
           [-o outfile] [@file] infile...

       Only the most useful options are listed here; see below for the
       remainder.  g++ accepts mostly the same options as gcc.

2.2 常用命令选项

无选项
对c/cpp文件进行预处理、编译、汇编、链接,形成可执行文件,默认生成的可执行文件名为a.out,若要指定输出文件名可使用-o

[vvvcxjvvv@localhost temp]$ ls
hello.cpp
[vvvcxjvvv@localhost temp]$ g++ hello.cpp
[vvvcxjvvv@localhost temp]$ ls
a.out  hello.cpp

-E
预处理指定源文件而不进行编译

[vvvcxjvvv@localhost GCC_Test]$ g++ -E hello.cpp -o hello.i

-S
告诉g++在为代码产生汇编文件后停止编译

[vvvcxjvvv@localhost GCC_Test]$ g++ -S hello.i -o hello.s

-c
编译、汇编指定源文件但不进行链接

[vvvcxjvvv@localhost GCC_Test]$ g++ -c hello.s -o hello.o

-std=c++11
设置编译标准

[vvvcxjvvv@localhost src]$ g++ -std=c++11 hello.cpp

-g
告诉 GCC 产生能被 GNU 调试器GDB使用的调试信息,以调试程序。

[vvvcxjvvv@localhost src]$ g++ -g hello.cpp

-o[n]
使用不同的优化级别编译程序,级别为0-3,级别越高优化效果越好但编译时间越长

  • -O0 表示不做优化
  • -O1 为默认优化
  • -O2 除了完成-O1的优化之外,还进行一些额外的调整工作,如指令调整等。
  • -O3 则包括循环展开和其他一些与处理特性相关的优化工作。
[vvvcxjvvv@localhost temp]$ g++ -o0 hello.cpp -o hello_o0
[vvvcxjvvv@localhost temp]$ g++ -o1 hello.cpp -o hello_o1
[vvvcxjvvv@localhost temp]$ g++ -o2 hello.cpp -o hello_o2
[vvvcxjvvv@localhost temp]$ ls
hello.cpp  hello_o0  hello_o1  hello_o2
[vvvcxjvvv@localhost temp]$ time ./hello_o0
Hello World!
Hello Linux!

real	0m0.004s
user	0m0.000s
sys	0m0.004s
[vvvcxjvvv@localhost temp]$ time ./hello_o1
Hello World!
Hello Linux!

real	0m0.002s
user	0m0.001s
sys	0m0.000s
[vvvcxjvvv@localhost temp]$ time ./hello_o2
Hello World!
Hello Linux!

real	0m0.003s
user	0m0.000s
sys	0m0.003s
[vvvcxjvvv@localhost temp]$ 

-Wall/-w

  • -wall:打印警报信息
  • -w:关闭警告信息
[vvvcxjvvv@localhost src]$ g++ -Wall test.cpp -o test_withwall
test.cpp: 在函数‘int main()’中:
test.cpp:5:6: 警告:未使用的变量‘a’ [-Wunused-variable]
  int a = 0;
      ^
test.cpp:6:6: 警告:未使用的变量‘b’ [-Wunused-variable]
  int b = 0;
      ^
[vvvcxjvvv@localhost src]$ g++ test.cpp -o test_withoutwall
[vvvcxjvvv@localhost src]$ g++ -w test.cpp -o test_withw
[vvvcxjvvv@localhost src]$ 

-I
指定头文件搜索目录

[vvvcxjvvv@localhost src]$ g++ -g -o2 say.cpp -o say
say.cpp:1:18: 致命错误:head.h:没有那个文件或目录
 #include <head.h>
                  ^
编译中断。
[vvvcxjvvv@localhost src]$ g++ -g -o2 -I../include/  say.cpp -o say
[vvvcxjvvv@localhost src]$ ./say
Hello World

-l
指定库文件,-l可以直接链接在/lib、/usr/lib和/usr/local/lib里的库

[vvvcxjvvv@localhost src]$ g++ -lglog hello.cpp #链接glog库

-L
指定库文件目录——如果库文件没放在/lib、/usr/lib、/usr/local/lib三个目录里,需要使用-L参数(大写)指定库文件所在目录

[vvvcxjvvv@localhost GCC_Demo]$ g++ main.cpp -lsayHello -Lsrc -Iinclude -o staticmain

-D
在使用gcc/g++编译的时候定义宏

#include <iostream>
using namespace std;

int main(){
	#ifdef _HELLO_
		cout << ("_HELLO_ has already been defined~") << endl;
	#endif
		
	cout << "Hello World" << endl;
	return 0;
	
}
[vvvcxjvvv@localhost src]$ g++ -D_HELLO_ define_test.cpp -o define_withD
[vvvcxjvvv@localhost src]$ g++ define_test.cpp -o define_withoutD
[vvvcxjvvv@localhost src]$ ./define_withD
_HELLO_ has already been defined~
Hello World
[vvvcxjvvv@localhost src]$ ./define_withoutD 
Hello World
[vvvcxjvvv@localhost src]$ 

-o
用于指定输出文件名

[vvvcxjvvv@localhost temp]$ ls
hello.cpp
[vvvcxjvvv@localhost temp]$ g++ -g hello.cpp
[vvvcxjvvv@localhost temp]$ ls
a.out  hello.cpp
[vvvcxjvvv@localhost temp]$ g++ -g hello.cpp -o hello
[vvvcxjvvv@localhost temp]$ ls
a.out  hello  hello.cpp
[vvvcxjvvv@localhost temp]$ 

三、多源文件编译方式对比

初始目录及源文件:

[vvvcxjvvv@localhost GCC_Demo]$ tree ./
./
├── include
│   └── sayHello.h
├── main.cpp
└── src
    └── sayHello.cpp

2 directories, 3 files

sayHello.h

#include <iostream>
using namespace std;

class myfriend{
private:
	string name;
public:
	myfriend(string a):name(a){};
	void say(){cout << "Hello " << name << endl;};

};

void sayHello(string name);

sayHello.cpp

#include <iostream>
#include "sayHello.h"
using namespace std;

void sayHello(string name){
	myfriend a(name);
	cout << "Haven't seen you for a long time" << endl;
	a.say();
}

main.cpp

#include <iostream>
#include "sayHello.h"
using namespace std;

int main(){
    sayHello("Chandler");
    return 0;
}

3.1 多源文件直接编译

[vvvcxjvvv@localhost GCC_Demo]$ tree ./
./
├── include
│   └── sayHello.h
├── main.cpp
└── src
    └── sayHello.cpp

2 directories, 3 files
[vvvcxjvvv@localhost GCC_Demo]$ g++ main.cpp src/sayHello.cpp -Iinclude/ -o main
[vvvcxjvvv@localhost GCC_Demo]$ ./main
Haven't seen you for a long time
Hello Chandler

3.2 库文件链接

有时候想将程序功能给别人用,又不想公开源代码,就可以考虑将源文件编译成库文件,同时给别人提供相关的一组头文件即可(生成的动态/静态库文件+头文件)。头文件(.h)只是对函数,变量,类的声明,而源文件(.cpp)才是模块真正实现的部分。头文件(.h)服务于源文件。

3.2.1 静态链接

linux中静态库后缀名是.a,而windows是.lib,并且静态链接库的名称遵循特定规则
对于linux静态库名字格式为libxxx.a,对于windows静态库名字格式为libxxx.lib(其中xxx为起的静态库名字)

生成静态库

#step1:生成目标文件
g++ -c 源文件
#step2:将目标文件生成静态库
ar rcs 静态库名称 目标文件

静态编译
gcc默认进行动态编译,加入-static参数通知编译器进行静态编译
方式一:

g++ -static 源文件 静态库文件 -o 程序

注意:静态库文件的顺序是在源文件之后,否则链接出错

方式二:

g++ -static 源文件 -L 静态库所在目录 -l静态库名

注:
-L 静态库所在目录
-l 静态链接库名字,后面不加空格。
对于windows,-l链接的静态库名字带lib前缀,而linux不带。例如windows下,静态库名字为libmodules.lib,则参数为-llibmodules,而对于linux静态库名字为libmodules.a,参数为-lmodules

[vvvcxjvvv@localhost GCC_Demo]$ tree ./
./
├── include
│   └── sayHello.h
├── main.cpp
└── src
    └── sayHello.cpp

2 directories, 3 files
[vvvcxjvvv@localhost GCC_Demo]$ cd src
[vvvcxjvvv@localhost src]$ g++ sayHello.cpp -c -I../include/ 
[vvvcxjvvv@localhost src]$ ls
sayHello.cpp  sayHello.o
[vvvcxjvvv@localhost src]$ ar rs libsayHello.a sayHello.o
ar: 正在创建 libsayHello.a
[vvvcxjvvv@localhost src]$ ls
libsayHello.a  sayHello.cpp  sayHello.o
[vvvcxjvvv@localhost src]$ cd ../
[vvvcxjvvv@localhost GCC_Demo]$ g++ main.cpp -lsayHello -Lsrc -Iinclude -o staticmain
[vvvcxjvvv@localhost GCC_Demo]$ tree ./
./
├── include
│   └── sayHello.h
├── main.cpp
├── src
│   ├── libsayHello.a
│   ├── sayHello.cpp
│   └── sayHello.o
└── staticmain

2 directories, 6 files
[vvvcxjvvv@localhost GCC_Demo]$ ./staticmain
Haven't seen you for a long time
Hello Chandler
[vvvcxjvvv@localhost GCC_Demo]$ 

步骤:

  1. 进入src目录下对sayHello.cpp文件进行汇编,生成sayHello.o文件
  2. 使用ar rs libsayHello.a sayHello.o将sayHello.o生成静态库libsayHello.a
  3. g++ main.cpp -lsayHello -Lsrc -Iinclude -o staticmain链接静态库生成可执行文件

3.2.2 动态链接

生成动态库
方式一:

g++ -fpic -shared 源文件名... -o 动态链接库名
  • -fpic (还可写成 -fPIC)选项的功能是,令 GCC 编译器生成动态链接库(多个目标文件的压缩包)时,表示各目标文件中函数、类等功能模块的地址使用相对地址,而非绝对地址。这样,无论将来链接库被加载到内存的什么位置,都可以正常使用;
  • -shared 选项用于生成动态链接库。在linux中动态库后缀名是.so,windows是.dll

方式二:

#step1:通过源文件产生目标文件
g++ -c -fpic 源文件名
#step2:生成动态库文件
g++ -shared 目标文件 -o 动态库名称
#step3:链接动态库
g++ 源文件 库文件 -o 程序

注:
若出现程序不能运行的问题,是因为找不到相应的动态库。需要将动态库文件拷贝到系统动态链接库文件夹再运行程序即可
windows系统动态链接库文件夹:C:\Windows\System32
Linux系统动态链接库文件夹:/lib

[vvvcxjvvv@localhost GCC_Demo]$ tree ./
./
├── include
│   └── sayHello.h
├── main.cpp
└── src
    └── sayHello.cpp

2 directories, 3 files
[vvvcxjvvv@localhost GCC_Demo]$ cd src
[vvvcxjvvv@localhost src]$ g++ sayHello.cpp -I../include -fPIC -shared -o libsayHello.so
[vvvcxjvvv@localhost src]$ ls
libsayHello.so  sayHello.cpp
[vvvcxjvvv@localhost src]$ cd ../
[vvvcxjvvv@localhost GCC_Demo]$ g++ main.cpp -Iinclude -Lsrc -lsayHello -o dynamicmain
[vvvcxjvvv@localhost GCC_Demo]$ ls
dynamicmain  include  main.cpp  src
[vvvcxjvvv@localhost GCC_Demo]$ tree ./
./
├── dynamicmain
├── include
│   └── sayHello.h
├── main.cpp
└── src
    ├── libsayHello.so
    └── sayHello.cpp

2 directories, 5 files
[vvvcxjvvv@localhost GCC_Demo]$ ./dynamicmain
./dynamicmain: error while loading shared libraries: libsayHello.so: cannot open shared object file: No such file or directory
[vvvcxjvvv@localhost GCC_Demo]$ LD_LIBRARY_PATH=src ./dynamicmain
Haven't seen you for a long time
Hello Chandler
[vvvcxjvvv@localhost GCC_Demo]$ 

步骤:

  1. 进入src目录下使用sayHello.cpp生成动态库libsayHello.so
  2. 将动态库进行链接生成可执行文件

注:g++ sayHello.cpp -I../include -fPIC -shared -o libsayHello.so可分解为以下两步:

  1. g++ sayHello.cpp -I…/include -c -fPIC
  2. g++ -shared -o libsayHello.so sayHello.o

3.2.3 静态库/动态库对比

静态库特点
一般扩展名为(.a或.lib),这类的函数库通常扩展名为libxxx.a或xxx.lib 。
这类库在编译的时候会直接整合到目标程序中,所以利用静态函数库编译成的文件会比较大,这类函数库最大的优点就是编译成功的可执行文件可以独立运行,而不再需要向外部要求读取函数库的内容;但是从升级难易度来看明显没有优势,如果函数库更新,需要重新编译。
动态库特点
动态函数库的扩展名一般为(.so或.dll),这类函数库通常名为libxxx.so或xxx.dll 。
与静态函数库被整个捕捉到程序中不同,动态函数库在编译的时候,在程序里只有一个“指向”的位置而已,也就是说当可执行文件需要使用到函数库的机制时,程序才会去读取函数库来使用;也就是说可执行文件无法单独运行。这样从产品功能升级角度方便升级,只要替换对应动态库即可,不必重新编译整个可执行文件。

GCC and Make Compiling, Linking and Building C/C++ Applications

A library is a collection of pre-compiled object files that can be linked into your programs via the linker. Examples are the system functions such as printf() and sqrt().
There are two types of external libraries: static library and shared library.Because of the advantage of dynamic linking, GCC, by default, links to the shared library if it is available.You can list the contents of a library via “nm filename”.

  1. A static library has file extension of “.a” (archive file) in Unixes or “.lib” (library) in Windows. When your program is linked against a static library, the machine code of external functions used in your program is copied into the executable. A static library can be created via the archive program “ar.exe”.
  2. A shared library has file extension of “.so” (shared objects) in Unixes or “.dll” (dynamic link library) in Windows. When your program is linked against a shared library, only a small table is created in the executable. Before the executable starts running, the operating system loads the machine code needed for the external functions - a process known as dynamic linking. Dynamic linking makes executable files smaller and saves disk space, because one copy of a library can be shared between multiple programs. Furthermore, most operating systems allows one copy of a shared library in memory to be used by all running programs, thus, saving memory. The shared library codes can be upgraded without the need to recompile your program.

总结

本文整理了GCC编译的相关知识,分析了gcc/g++编译代码的过程,展示了常用命令的使用。在多源文件编译下对比了直接编译、静态链接、动态链接的区别,并比较了动态库、静态库的特点

参考资料

  1. g++生成和使用c++动态/静态链接库教程
  2. GCC 编译 C(C++)静态链接库(gcc -L、gcc -l)和动态链接库(gcc -fPIC -shared)的创建和使用
  3. linux环境变量LIBRARY_PATH和LD_LIBRARY_PATH
  4. GCC and Make Compiling, Linking and Building C/C++ Applications

网站公告

今日签到

点亮在社区的每一天
去签到