[TOC]
# 更新gcc
红帽其实已经编译好了高版本的gcc,但未更新到base和epel这两个常用的源中,而是放在`scl`中。第一步便是安装`scl`:
~~~
yum install -y centos-release-scl glibc-static
~~~
如果你之前用过`grouplist/install`等命令,应该知道gcc包含在`Development Tools`这个组中。那么`scl`中的gcc/g++软件包的前缀都是`devtoolset`,也就不难理解了。安装gcc 6版本的命令是:
~~~
yum install -y devtoolset-8-gcc devtoolset-8-gcc-c++
~~~
`devtoolset-6`中的gcc版本为gcc 6,除此之外还有如下版本:
* devtoolset-3: gcc 4.9
* devtoolset-4: gcc 5
* devtoolset-6: gcc 6
* devtoolset-7: gcc 7
* devtoolset-8: gcc 8
至于为什么没有devtoolset-5,我也不清楚,估计是包含在devtoolset-4中了吧。值得说明的是这些软件包可以同时安装,不会相互覆盖和冲突,也不会覆盖系统的版本。即可以在系统中同时安装gcc 6, gcc 7, gcc 8等多个版本。
因为不会覆盖系统默认的gcc,使用这些软件的方法有四种:
1. 使用绝对路径;
2. 添加可执行文件路径到PATH环境变量;
3. 使用官方推荐的加载命令:`scl enable devtoolset-x bash`, x为要启用的版本;
4. 执行安装软件自带的脚本: `source /opt/rh/devtoolset-x/enable`,x为要启用的版本。
推荐使用最后两种方式,例如启用gcc 6: `source /opt/rh/devtoolset-6/enable`,然后输入`gcc -v`查看版本已经变成gcc 6.3.1。使用类似的命令可以随时在多个gcc版本中切换。如果希望长期使用高版本,可将此命令写入`.bashrc`等配置文件。
~~~
echo "source /opt/rh/devtoolset-8/enable" >>/etc/profile
~~~
最后说一下,scl以及scl-rh源中的软件包都安装在/opt/rh/目录下,包含可执行文件、配置等。所以启用命令的路径是`/opt/rh/xxx/enable`,安装的服务重启命令则可能是`systemctl restart rh-xxx`,需要加rh或scl前缀以区别其他源的包。如果你用过remi/gitlab等源,其行为方式也是类似的。
# 工作流
gcc编译器从拿到一个c源文件到生成一个可执行程序,中间一共经历了四个步骤:

四个步骤并不是gcc独立完成的,而是在内部调用了其他工具,从而完成了整个工作流程:

gcc工作的流程
> deng@itcast:~/share/3rd/1gcc$ ls 1hello.c
>
> 第一步: 进行预处理
>
> deng@itcast:~/share/3rd/1gcc$ gcc -E 1hello.c -o 1hello.i
>
> 第二步: 生成汇编文件
>
> deng@itcast:~/share/3rd/1gcc$ gcc -S 1hello.i -o 1hello.s
>
> 第三步: 生成目标代码
>
> deng@itcast:~/share/3rd/1gcc$ gcc -c 1hello.s -o 1hello.o
>
> 第四步: 生成可以执行文件
>
> deng@itcast:~/share/3rd/1gcc$ gcc 1hello.o -o 1hello 第五步: 执行 deng@itcast:~/share/3rd/1gcc$ ./1hello hello itcast
直接将源文件生成一个可以执行文件
> deng@itcast:~/share/3rd/1gcc$ gcc 1hello.c -o 1hello deng@itcast:~/share/3rd/1gcc$ ./1hello hello itcast
如果不指定输出文件名字, gcc编译器会生成一个默认的可以执行a.out
> deng@itcast:~/share/3rd/1gcc$ gcc 1hello.c
> deng@itcast:~/share/3rd/1gcc$ ls 1hello 1hello.c 1hello.i 1hello.o 1hello.s a.out deng@itcast:~/share/3rd/1gcc$ ./a.out
> hello itcast
# 常用选项
| **选项** | **作用** |
| --- | --- |
| \-o file | 指定生成的输出文件名为file |
| \-E | 只进行预处理 |
| \-S(大写) | 只进行预处理和编译 |
| \-c(小写) | 只进行预处理、编译和汇编 |
| \-v / --version | 查看gcc版本号 |
| \-g | 包含调试信息 |
| \-On n=0~3 | 编译优化,n越大优化得越多 |
| \-Wall | 提示更多警告信息 |
| \-D | 编译时定义宏 |
显示所有的警告信息
> gcc -Wall test.c
将警告信息当做错误处理
> gcc -Wall -Werror test.c
测试程序(-D选项):
~~~
#include <stdio.h>
int main(void)
{
printf("SIZE: %d\n", SIZE);
return 0;
}
~~~
~~~
deng@itcast:~/test$ gcc 2test.c -DSIZE=10
deng@itcast:~/test$ ./a.out
SIZE: 10
~~~
# 静态链接和动态链接
链接分为两种:**静态链接**、**动态链接**。
**静态、动态编译对比**
前面我们编写的应用程序大量用到了标准库函数,系统默认采用动态链接的方式进行编译程序,若想采用静态编译,加入-static参数。
以下是分别采用动态编译、静态编译时文件对比:
测试程序(test.c)如下:
~~~
#include <stdio.h>
int main(void) {
printf("hello world \n");
return 0;
}
~~~
编译:
> deng@itcast:~/test$ gcc test.c -o test\_share
>
> deng@itcast:~/test$ gcc -static test.c -o test\_static
# 静态库和动态库
所谓“程序库”,简单说,就是包含了数据和执行码的文件。其不能单独执行,可以作为其它执行程序的一部分来完成某些功能。
库的存在可以使得程序模块化,可以加快程序的再编译,可以实现代码重用,可以使得程序便于升级。
程序库可分**静态库(static library)**和**共享库(shared library)**
## 静态库制作
静态库可以认为是一些目标代码的集合,是在可执行程序运行前就已经加入到执行码中,成为执行程序的一部分。
按照习惯,一般以“.a”做为文件后缀名。静态库的命名一般分为三个部分:
* 前缀:lib
* 库名称:自己定义即可
* 后缀:.a
所以最终的静态库的名字应该为:**libxxx.a**

步骤1:将c源文件生成对应的.o文件
> deng@itcast:~/test/3static\_lib$ gcc -c add.c -o add.o
> deng@itcast:~/test/3static\_lib$ gcc -c sub.c -o sub.o
> deng@itcast:~/test/3static\_lib$ gcc -c mul.c -o mul.o
> deng@itcast:~/test/3static\_lib$ gcc -c div.c -o div.o
步骤2:使用打包工具ar将准备好的.o文件打包为.a文件 libtest.a
> deng@itcast: ar -rcs libtest.a add.o sub.o mul.o div.o
**在使用ar工具是时候需要添加参数:rcs**
* r更新
* c创建
* s建立索引
**静态库使用**
静态库制作完成之后,需要将.a文件和头文件一起发布给用户。
假设测试文件为main.c,静态库文件为libtest.a头文件为head.h
编译命令:
> deng@itcast: gcc test.c -L./ -I./ -ltest -o test
参数说明:
* \-L:表示要连接的库所在目录
* \-I./: I(大写i) 表示指定头文件的目录为当前目录
* \-l(小写L):**指定链接时需要的库,去掉前缀和后缀**

add.h
~~~
#ifndef __ADD_H__
#define __ADD_H__
int add(int x, int y);
#endif /* __ADD_H__ */
~~~
add.c
~~~
#include "add.h"
int add(int x, int y) {
return x + y;
}
~~~
test.c
~~~
#include <stdio.h>
#include "add.h"
int main(void) {
printf("1 + 2 = %d\n", add(1, 2));
return 0;
}
~~~
## 动态库制作
共享库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入。不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例,规避了空间浪费问题。
动态库在程序运行是才被载入,也解决了静态库对程序的更新、部署和发布页会带来麻烦。用户只需要更新动态库即可,增量更新。
按照习惯,一般以“.so”做为文件后缀名。共享库的命名一般分为三个部分:
* 前缀:lib
* 库名称:自己定义即可
* 后缀:.so
所以最终的动态库的名字应该为:`libxxx.so`

**1)动态库制作**
步骤一:生成目标文件,此时要加编译选项:-fPIC(fpic)
> deng@itcast:~/test/5share\_lib$ gcc -fPIC -c add.c
> deng@itcast:~/test/5share\_lib$ gcc -fPIC -c sub.c
> deng@itcast:~/test/5share\_lib$ gcc -fPIC -c mul.c
> deng@itcast:~/test/5share\_lib$ gcc -fPIC -c div.c
参数:-fPIC 创建与地址无关的编译程序(pic,position independent code),是为了能够在多个应用程序间共享。
步骤二:生成共享库,此时要加链接器选项: -shared(指定生成动态链接库)
> deng@itcast:~/test/5share\_lib$ gcc -shared add.o sub.o mul.o div.o -o libtest.so
步骤三: 通过nm命令查看对应的函数
> deng@itcast: nm libtest.so | grep add
> deng@itcast: nm libtest.so | grep sub
ldd查看可执行文件的依赖的动态库
> deng@itcast:~/share/3rd/2share\_test$ ldd test
> linux-vdso.so.1 => (0x00007ffcf89d4000) libtest.so => /lib/libtest.so (0x00007f81b5612000) libc.so.6 => /lib/x86\_64-linux-gnu/libc.so.6 (0x00007f81b5248000) /lib64/ld-linux-x86-64.so.2 (0x00005562d0cff000)
**2)动态库测试**
引用动态库编译成可执行文件(跟静态库方式一样)
> deng@itcast:~/test/6share\_test$ gcc test.c -L. -I. -ltest (-I. 大写i -ltest 小写L)
然后运行:./a.out,发现竟然报错了!!!

* 当系统加载可执行代码时候,能够知道其所依赖的库的名字,但是还需要知道绝对路径。此时就需要系统动态载入器(dynamic linker/loader)。
* 对于elf格式的可执行程序,是由`ld-linux.so`来完成的,它先后搜索elf文件的 DT\_RPATH段 — 环境变量LD\_LIBRARY\_PATH — /etc/ld.so.cache文件列表 —**/lib/, /usr/lib**目录找到库文件后将其载入内存。
**3)如何让系统找到动态库**
* 拷贝自己制作的共享库到/lib或者/usr/lib(不能是/lib64目录)
* 临时设置LD\_LIBRARY\_PATH:
> export LD\_LIBRARY\_PATH=$LD\_LIBRARY\_PATH:库路径
* 永久设置,把export LD\_LIBRARY\_PATH=$LD\_LIBRARY\_PATH:库路径,设置到~/.bashrc或者 /etc/profile文件中
> deng@itcast:~/share/3rd/2share\_test$ vim ~/.bashrc
>
> 最后一行添加如下内容:
>
> export LD\_LIBRARY\_PATH=$LD\_LIBRARY\_PATH:/home/deng/share/3rd/2share\_test
使环境变量生效
> deng@itcast: source ~/.bashrc
> deng@itcast: ./test
> a + b = 20 a - b = 10
* 将其添加到 /etc/ld.so.conf文件中
编辑/etc/ld.so.conf文件,加入库文件所在目录的路径
运行sudo ldconfig -v,该命令会重建/etc/ld.so.cache文件
> deng@itcast: sudo vim /etc/ld.so.conf
>
> 文件最后添加动态库路径(绝对路径)

* > 使生效
> deng@itcast: sudo ldconfig -v
或者使用符号链接, 但是一定要使用绝对路径
> deng@itcast: sudo ln -s /home/deng/test/6share\_test/libtest.so /lib/libtest.so
- c语言
- 基础知识
- 变量和常量
- 宏定义和预处理
- 随机数
- register变量
- errno全局变量
- 静态变量
- 类型
- 数组
- 类型转换
- vs中c4996错误
- 数据类型和长度
- 二进制数,八进制数和十六进制数
- 位域
- typedef定义类型
- 函数和编译
- 函数调用惯例
- 函数进栈和出栈
- 函数
- 编译
- sizeof
- main函数接收参数
- 宏函数
- 目标文件和可执行文件有什么
- 强符号和弱符号
- 什么是链接
- 符号
- 强引用和弱引用
- 字符串处理函数
- sscanf
- 查找子字符串
- 字符串指针
- qt
- MFC
- 指针
- 简介
- 指针详解
- 案例
- 指针数组
- 偏移量
- 间接赋值
- 易错点
- 二级指针
- 结构体指针
- 字节对齐
- 函数指针
- 指针例子
- main接收用户输入
- 内存布局
- 内存分区
- 空间开辟和释放
- 堆空间操作字符串
- 内存处理函数
- 内存分页
- 内存模型
- 栈
- 栈溢出攻击
- 内存泄露
- 大小端存储法
- 寄存器
- 结构体
- 共用体
- 枚举
- 文件操作
- 文件到底是什么
- 文件打开和关闭
- 文件的顺序读写
- 文件的随机读写
- 文件复制
- FILE和缓冲区
- 文件大小
- 插入,删除,更改文件内容
- typeid
- 内部链接和外部链接
- 动态库
- 调试器
- 调试的概念
- vs调试
- 多文件编程
- extern关键字
- 头文件规范
- 标准库以及标准头文件
- 头文件只包含一次
- static
- 多线程
- 简介
- 创建线程threads.h
- 创建线程pthread
- gdb
- 简介
- mac使用gdb
- setjump和longjump
- 零拷贝
- gc
- 调试器原理
- c++
- c++简介
- c++对c的扩展
- ::作用域运算符
- 名字控制
- cpp对c的增强
- const
- 变量定义数组
- 尽量以const替换#define
- 引用
- 内联函数
- 函数默认参数
- 函数占位参数
- 函数重载
- extern "C"
- 类和对象
- 类封装
- 构造和析构
- 深浅拷贝
- explicit关键字
- 动态对象创建
- 静态成员
- 对象模型
- this
- 友元
- 单例
- 继承
- 多态
- 运算符重载
- 赋值重载
- 指针运算符(*,->)重载
- 前置和后置++
- 左移<<运算符重载
- 函数调用符重载
- 总结
- bool重载
- 模板
- 简介
- 普通函数和模板函数调用
- 模板的局限性
- 类模板
- 复数的模板类
- 类模板作为参数
- 类模板继承
- 类模板类内和类外实现
- 类模板和友元函数
- 类模板实现数组
- 类型转换
- 异常
- 异常基本语法
- 异常的接口声明
- 异常的栈解旋
- 异常的多态
- 标准异常库
- 自定义异常
- io
- 流的概念和类库结构
- 标准io流
- 标准输入流
- 标准输出流
- 文件读写
- STL
- 简介
- string容器
- vector容器
- deque容器
- stack容器
- queue容器
- list容器
- set/multiset容器
- map/multimap容器
- pair对组
- 深浅拷贝问题
- 使用时机
- 常用算法
- 函数对象
- 谓词
- 内建函数对象
- 函数对象适配器
- 空间适配器
- 常用遍历算法
- 查找算法
- 排序算法
- 拷贝和替换算法
- 算术生成算法
- 集合算法
- gcc
- GDB
- makefile
- visualstudio
- VisualAssistX
- 各种插件
- utf8编码
- 制作安装项目
- 编译模式
- 内存对齐
- 快捷键
- 自动补全
- 查看c++类内存布局
- FFmpeg
- ffmpeg架构
- 命令的基本格式
- 分解与复用
- 处理原始数据
- 录屏和音
- 滤镜
- 水印
- 音视频的拼接与裁剪
- 视频图片转换
- 直播
- ffplay
- 常见问题
- 多媒体文件处理
- ffmpeg代码结构
- 日志系统
- 处理流数据
- linux
- 系统调用
- 常用IO函数
- 文件操作函数
- 文件描述符复制
- 目录相关操作
- 时间相关函数
- 进程
- valgrind
- 进程通信
- 信号
- 信号产生函数
- 信号集
- 信号捕捉
- SIGCHLD信号
- 不可重入函数和可重入函数
- 进程组
- 会话
- 守护进程
- 线程
- 线程属性
- 互斥锁
- 读写锁
- 条件变量
- 信号量
- 网络
- 分层模型
- 协议格式
- TCP协议
- socket
- socket概念
- 网络字节序
- ip地址转换函数
- sockaddr数据结构
- 网络套接字函数
- socket模型创建流程图
- socket函数
- bind函数
- listen函数
- accept函数
- connect函数
- C/S模型-TCP
- 出错处理封装函数
- 多进程并发服务器
- 多线程并发服务器
- 多路I/O复用服务器
- select
- poll
- epoll
- epoll事件
- epoll例子
- epoll反应堆思想
- udp
- socket IPC(本地套接字domain)
- 其他常用函数
- libevent
- libevent简介