前言
什么是GCC
GCC原名为 GNU
C语言编译器
「GCC」(GNU Compiler Collection,GNU编译套件)
是由GNU开发的编程语言编译器。
正文
安装命令
代码语言:javascript复制sudo apt-get insatll gcc g
注意安装版本要大于4.8.5因为4.8.5以后的版本才支持c 11标准
查看版本
代码语言:javascript复制gcc -v
gcc --version
g -v
g --version
gcc和g 的区别
gcc和g 都是GNU (组织)的一个编译器。
■ 「误区一」: gcc只能编译c代码,g 只能编译c 代码。
「两者都可以」,请注意:
- 「后缀为.c」的,gcc把它当作是「C程序」,而g 当作是c 程序
- 「后缀为.cpp」 的,两者都会认为是「C 程序」,C 的语法规则更加严谨一些
- 编译阶段,g 会调用gcc, 对于C 代码,两者是等价的,但是因为
gcc命令不能自动和C 程序使用的库联接,所以通常用g 来完成链接
,为了统一起见,干脆编译/链接统统用g 了 ,这就给人一种错觉,好像 cpp 程序只能用 g 似的.
■ 误区二: gcc不会定义_cplusplus
宏,而g 会
- 实际上,这个宏只是
标志着编译器将会把代码按C还是C 语法来解释
- 如上所述,如果「后缀为.c」,并且采用gcc编译器,则该宏就是未定义的,否则,就是已定义
■ 误区三:编译只能用gcc,链接只能用g
- 严格来说,这句话不算错误,但是它混淆了概念,应该这样说:
编译可以用gcc/g ,而链接可以用g 或者gcc -lstdc
。 gcc命令不能自动和C 程序使用的库联接
,所以通常使用g 来完成联接,但在编译阶段,g 会自动调用gcc,二者等价.
gcc编译过程
在这里插入图片描述
gcc常用参数
选项名 | 作用 |
---|---|
-o | 产生目标(.i、.s、.o、可执行文件等) |
-E | 只运行C预编译器 |
-S | 告诉编译器产生汇编程序文件后停止编译,产生的汇编语言文件拓展名为.s |
-c | 通知gcc取消连接步骤,即编译源码,并在最后生成目标文件 |
-w | 不产生任何警告信息 |
-Wall | 使gcc对源文件的代码有问题的地方发出警告 |
-Idir | 指定 include 包含文件的搜索目录 |
-Ldir | 指定编译的时候,搜索的库的路径。 |
-lLib | 在程序编译的时候,指定使用的库 |
-g | 在目标文件中嵌入调试信息,以便gdb之类的调试程序调试 |
-D | 允许从编译程序命令行进行宏定义符号 |
gcc的使用示例:
代码语言:javascript复制gcc -E hello.c -o hello.i #对hello.c文件进行预处理,生成了hello.i 文件
gcc -S hello.i -o hello.s #对预处理文件进行编译,生成了汇编文件
gcc -c hello.s -o hello.o #对汇编文件进行编译,生成了目标文件
gcc hello.o -o hello #对目标文件进行链接,生成可执行文件
gcc hello.c -o hello #直接编译链接成可执行目标文件
gcc -c hello.c 或 gcc -c hello.c -o hello.o #编译生成可重定位目标文件
「-D参数演示」
测试代码如下:
代码语言:javascript复制#include<stdio.h>
int main()
{
#ifdef DEBUG
printf("DEBUG:n");
#else
printf("Normal:n");
#endif
for(int i=0;i<3;i )
printf("workn");
return 0;
}
测试命令
代码语言:javascript复制gcc -o Debug Debug.c
./Debug
Normal:
work
work
work
代码语言:javascript复制gcc -o Debug Debug.c -DDEBUG
./Debug
DEBUG:
work
work
work
库的介绍
「什么是库?」
库文件是计算机上的一类文件,可以简单的把库文件看成一种代码仓库,它提供给使用者一些可以直接拿来用的变量、函数或类。
库是特殊的一种程序,编写库的程序和编写一般的程序区别不大,只是库不能单独运行。
库文件有两种,静态库和动态库(共享库)
「静态库(.a)」:程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库。
静态库比较占用磁盘空间,而且程序不可以共享静态库。运行时也是比较占内存的,因为每个程序都包含了一份静态库。
「动态库(.so或.sa)」:程序在运行的时候才去链接共享库的代码,多个程序共享使用库的代码,这样就减少了程序的体积。
「库的好处」:
1.代码保密
2.方便部署和分发
生成静态库
「静态库命名规则:」
◆ 「Linux」 : libxxx.a
lib : 前缀(固定)
xxx : 库的名字,自己起 .
a : 后缀(固定)
◆ 「Windows」 : libxxx.lib
Linux生成静态库
首先准备几个文件和文件夹,文件树形结构
代码语言:javascript复制[root@deroy]# tree
.
├── calc
│ ├── add.c
│ ├── div.c
│ ├── head.h
│ ├── main.c
│ ├── mult.c
│ └── sub.c
└── library
├── include
├── lib
└── src
「add.c」
代码语言:javascript复制#include<stdio.h>
#include"head.h"
int add(int a,int b)
{
return a b;
}
「div.c」
代码语言:javascript复制#include<stdio.h>
#include"head.h"
double divide(int a,int b)
{
if(b==0)
return (double)a/b;
else
return 0;
}
「mult.c」
代码语言:javascript复制#include<stdio.h>
#include"head.h"
int multiply(int a,int b)
{
return a*b;
}
「sub.c」
代码语言:javascript复制#include<stdio.h>
#include"head.h"
int subtract(int a,int b)
{
return a-b;
}
「head.h」
代码语言:javascript复制#ifndef _HEAD_H_
#define _HEAD_H_
int add(int a,int b);
double divide(int a,int b);
int multiply(int a,int b);
int subtract(int a,int b);
#endif
为了生成 「.a 文件」,我们需要先生成 「.o文件」。
代码语言:javascript复制[root@deroy]# cd calc/
[root@calc]# gcc -c add.c div.c mult.c sub.c
打包生成静态库
代码语言:javascript复制[root@calc]# ar rcs libcalc.a add.o sub.o mult.o div.o
ar 是 gun 归档工具,rcs 表示 replace and create ,如果 libhello 之前存在,将创建新的 libhello.a 并将其替换。
r - 将文件插入备存文件中 c - 建立备存文件 s - 索引
「将库放到指定位置」
代码语言:javascript复制[root@calc]# cp libcalc.a ../library/lib/
[root@calc]# cp head.h ../library/include/
[root@calc]# cp add.c div.c mult.c sub.c ../library/src/
库目录结构如下(这个库目录就是发给被人用的)
[root@ecs-x-medium-2-linux-20200312093025 library]# tree
.
├── include
│ └── head.h
├── lib
│ └── libcalc.a
└── src
├── add.c
├── div.c
├── mult.c
└── sub.c
使用静态库
编辑 main.c 文件
代码语言:javascript复制#include<stdio.h>
#include"head.h"
int main()
{
int a = 20;
int b = 12;
printf("a = %d,b = %dn",a,b);
printf("a b = %dn",add(a,b));
printf("a - b = %dn",subtract(a,b));
printf("a * b = %dn",multiply(a,b));
printf("a / b = %dn",divide(a,b));
return 0;
}
然后就可以这样来使用静态库libcalc.a
[root@library]# gcc main.c -o app -I ./include/ -lcalc -L./lib
[root@library]# ./app
a = 20,b = 12
a b = 32
a - b = 8
a * b = 240
a / b = 12
代码语言:javascript复制其中:
-I directory 指定include包含文件的搜索目录
-l 在程序编译的时候,指定使用的库
-L directory 指定编译的时候,搜索的库的路径
生成动态库(共享库)
动态库命名规则:
「Linux : libxxx.so」
「lib : 前缀(固定)」
「xxx : 库的名字,自己起」 .
「so : 后缀(固定)」
「在Linux下是一个可执行文件」
Windows : libxxx.dll
使用静态库的测试代码,库目录结构还是一样
代码语言:javascript复制[root@deroy]# tree
.
├── calc
│ ├── add.c
│ ├── div.c
│ ├── head.h
│ ├── main.c
│ ├── mult.c
│ └── sub.c
└── library
├── include
├── lib
└── src
为了生成 「.so」 文件,我们需要先生成 「.o」 文件得到和位置无关的代码。
代码语言:javascript复制[root@calc]# gcc -c -fpic add.c div.c mult.c sub.c
打包生成「动态库」
代码语言:javascript复制[root@calc]# gcc -shared add.o sub.o mult.o div.o -o libcalc.so
「将库放到指定位置」
代码语言:javascript复制[root@calc]# cp libcalc.so ../library/lib/
[root@calc]# cp head.h ../library/include/
[root@calc]# cp add.c div.c mult.c sub.c ../library/src/
库目录结构如下(这个库目录就是发给被人用的)
[root@library]# tree
.
├── include
│ └── head.h
├── lib
│ └── libcalc.so
└── src
├── add.c
├── div.c
├── mult.c
└── sub.c
使用动态库
代码语言:javascript复制[root@calc]# gcc main.c -o app -I include/ -L lib/ -l calc
[root@calc]# ldd app
linux-vdso.so.1 => (0x00007ffc75cb0000)
libcalc.so => not found
libc.so.6 => /lib64/libc.so.6 (0x00007f3605ddb000)
/lib64/ld-linux-x86-64.so.2 (0x00007f36061a9000)
通过ldd命令查看程序动态库依赖关系
ldd是list dynamic dependencies
的缩写,意思是列出动态库依赖关系。
结果发现libcalc.so => not found
找不到了
「那么如何让程序找到依赖库呢?这里提供四种方法」
方法一(不推荐)
代码语言:javascript复制#拷贝.so文件到系统共享库路径下,一般指/usr/lib或者/lib/目录
sudo cp ./lib/libcalc.so /usr/lib/
方法二(临时环境变量)
代码语言:javascript复制[root@library]# export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/root/deroy/library/lib
[root@library]# echo $LD_LIBRARY_PATH
:/root/deroy/library/lib
[root@library]# ldd app
linux-vdso.so.1 => (0x00007fffe1570000)
libcalc.so => /root/deroy/library/lib/libcalc.so (0x00007f007020f000)
libc.so.6 => /lib64/libc.so.6 (0x00007f006fe41000)
/lib64/ld-linux-x86-64.so.2 (0x00007f0070411000)
[root@library]# ./app
a = 20,b = 12
a b = 32
a - b = 8
a * b = 240
a / b = 12
运行成功
缺陷:只在当前终端有效,关闭中端后就没用了
方法三(配置用户环境变量)
代码语言:javascript复制将环境变量写入到
~/.bashrc
即可,即将下面内容添加到末尾export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/root/deroy/library/lib
[root@library]# cd ~
[root@~]# vim .bashrc
[root@~]# . .bashrc #相当于source .bashrc
[root@~]# source .bashrc
[root@~]# cd deroy/library/
[root@library]# ldd app
linux-vdso.so.1 => (0x00007ffe0d2db000)
libcalc.so => /root/deroy/library/lib/libcalc.so (0x00007f937669c000)
libc.so.6 => /lib64/libc.so.6 (0x00007f93762ce000)
/lib64/ld-linux-x86-64.so.2 (0x00007f937689e000)
[root@library]# ./app
a = 20,b = 12
a b = 32
a - b = 8
a * b = 240
a / b = 12
方法四(配置系统环境变量)
代码语言:javascript复制将环境变量写入到
~/etc/profile
即可,即将下面内容添加到末尾,需要root权限export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/root/deroy/library/lib
[root@library]# vim /etc/profile
[root@library]# source /etc/profile
[root@library]# ldd app
linux-vdso.so.1 => (0x00007ffe08dc3000)
libcalc.so => /root/deroy/library/lib/libcalc.so (0x00007f0eada81000)
libc.so.6 => /lib64/libc.so.6 (0x00007f0ead6b3000)
/lib64/ld-linux-x86-64.so.2 (0x00007f0eadc83000)
[root@library]# ./app
a = 20,b = 12
a b = 32
a - b = 8
a * b = 240
a / b = 12
总结
静态库的优缺点
「优点」
◆ 静态库被打包到应用程序中加载速度快
◆ 发布程序无需提供静态库,移植方便
「缺点」
◆ 消耗系统资源,浪费内存 ◆ 更新、部署、发布麻烦
动态库的优缺点
「优点」
◆ 可以实现进程间资源共享(共享库)
◆ 更新、部署、发布简单
◆ 可以控制何时加载动态库
「缺点」
◆ 加载速度比静态库慢
◆ 发布程序时需要提供依赖的动态库
发送「关键字」获取「Linux安装配置视频」
和「GCC详细使用视频」