内容简介:预处理->编译->汇编->链接调用cpp做宏替换, 展开include文件, 把 .c文件 转 .i文件gcc -E a.c-o a.i
预处理->编译->汇编->链接
预处理
调用cpp做宏替换, 展开include文件, 把 .c文件 转 .i文件
gcc -E a.c-o a.i
编译
调用ccl, 把 .i文件 转 .s 汇编代码
gcc -S a.i -o a.s
汇编
调用as, 把.s文件 转 .o 二进制机械指令
gcc -c a.s -o a.o
链接
调用ld,把.o文件和调用的库代码链接为 .out可执行文件
gcc a.o -o a.out
实践
文件2main.c内容
#include <stdio.h> extern int max(int, int); int main() { printf("x > y ? %d", max(20,3)); return 0; }
文件2.c内容
int max(int x, int y) { return x > y ? 1 : 0; }
编译 2main.c 得到目标文件 2main.o
[root@localhost ~]# vi 2main.c [root@localhost ~]# gcc -E 2main.c -o 2main.i [root@localhost ~]# gcc -S 2main.i -o 2main.s [root@localhost ~]# gcc -c 2main.s -o 2main.o
编译 2.c 得到目标文件 2.o
[root@localhost ~]# gcc -E 2.c -o 2.i [root@localhost ~]# gcc -S 2.i -o 2.s [root@localhost ~]# gcc -c 2.s -o 2.o
链接两个目标文件
[root@localhost ~]# gcc 2.o 2main.o -o 2main.out
执行结果
[root@localhost ~]# ./2main.out x > y ? 1
快速编译链接
gcc 2.c 2main.c -o 2main.out
[root@localhost ~]# gcc 2.c 2main.c -o 3main.out [root@localhost ~]# ./3main.out x > y ? 1
检错
查看报错
-pedantic 检查被编译代码是否符合ANSI/ISO C标准
-Wall 产生警告信息
-Werror 警告信息当作错误来中断编译
语言标准
-ansi 等价于C的-std=c90. C++的-std=c++98.
-std=以下选项之一
`c90'
`c89'
`iso9899:1990'
`c90'
`c89'
`iso9899:1990' 所有ISO C90程序,C代码时等价于-ansi,
`iso9899:199409' ISO C90 修订版1
`c99' 不一定完全支持
`c11' 特性没确定好
`gnu90'
`gnu89' (C的默认选项)ISO C90和一部分C99特性,等价于c90
`gnu99' (下一个默认选项, 当C99完全支持时)ISO C99,等价于c99
`gnu11'
`c++98' 1998 ISO C++标准, C++代码时等价于-ansi
`gnu++98' (C++默认选项) 等同于c++98
`c++11'
`gnu++11'
c++1y
下一个新版本
c++14
编译器优化选项
-O0 不优化
-O1 至 -O3 优化级别加强
-Os 优化程序尺寸, 打开了大部分O2优化中不会增加程序大小的优化选项。关闭了通常不需要的优化选项:
-falign-functions -falign-jumps -falign-loops -falign-labels -freorder-blocks -fprefetch-loop-arrays
O0选项不进行任何优化,在这种情况下,编译器尽量的缩短编译消耗(时间,空间),此时,debug会产出和程序预期的结果。当程序运行被断点打断,此时程序内的各种声明是独立的,我们可以任意的给变量赋值,或者在函数体内把程序计数器指到其他语句,以及从源程序中 精确地获取你期待的结果.
O1优化会消耗少多的编译时间,它主要对代码的分支,常量以及表达式等进行优化。
O2会尝试更多的寄存器级的优化以及指令级的优化,它会在编译期间占用更多的内存和编译时间。
O3在O2的基础上进行更多的优化,例如使用伪寄存器网络,普通函数的内联,以及针对循环的更多优化。
Os主要是对代码大小的优化,我们基本不用做更多的关心。 通常各种优化都会打乱程序的结构,让调试工作变得无从着手。并且会打乱执行顺序,依赖内存操作顺序的程序需要做相关处理才能确保程序的正确性。
优化代码有可能带来的问题 ?
1.调试问题:任何级别的优化都将带来代码结构的改变。例如:对分支的合并和消除,对公用子表达式的消除,对循环内load/store操作的替换和更改等,都将会使目标代码的执行顺序变得面目全非,导致调试信息严重不足。
2.内存操作顺序改变所带来的问题:在O2优化后,编译器会对影响内存操作的执行顺序。例如:-fschedule-insns允许数据处理时先完成其他的指令;-fforce-mem有可能导致内存与寄存器之间的数据产生类似脏数据的不一致等。对于某些依赖内存操作顺序而进行的逻辑,需要做严格的处理后才能进行优化。例如,采用volatile关键字限制变量的操作方式,或者利用barrier迫使cpu严格按照指令序执行的。
动态库链接兼容
同时有C和C++两种文件, -lstdc++ 指定用libstdc++.so这个动态库, 省略"lib"和".so"则是"stdc++";
gcc 1.cpp -o 1.out -Wall -lstdc++ -std=c++14 -O3
静态库
生成
ar crv libmylib.a add.o sub.o mul.o div.o logger.o
返显:
a - add.o
a - sub.o
a - mul.o
a - div.o
a - logger.o
ar 命令: 归档目标文件,生成静态库
参数 c : 如果需要生成新的库文件, 不要警告
参数 r : rewrite代替库中现有的文件或插入新的文件
参数 v : 输出详细信息
ar t limylib.a 查看静态库 libmylib.a 中包含的目标文件
ar --help
链接
gcc test.c -L/home/SourceCode -lMath -static -o test
在/home/SourceCode搜索库文件libMath.a(默认是动态库优先, -static指定静态库)
静态库链接时搜索路径顺序:
- ld会去找GCC命令中的参数-L
- 再找环境变量LIBRARY_PATH 指定的gcc静态链接库文件搜索路径
- 最后找当初compile gcc时写在程序内的目录 /lib, /usr/lib, /usr/local/lib
动态库
gcc -fPIC -c math.c -o math.o 生成目标文件
gcc -shared math.o -o libMath.so 生成动态库
gcc -fPIC -shared math.c -o libMath.so 一行快速生成动态库
要求:运行期 export LD_LIBRARY_PATH=path指定环境变量,否则找不到动态库的位置
动态链接时、执行时搜索路径顺序:
- 编译目标代码时指定的动态库搜索路径
- 环境变量LD_LIBRARY_PATH指定的动态库搜索路径
- 配置文件/etc/ld.so.conf中指定的动态库搜索路径
- 默认的动态库搜索路径/lib
- 默认的动态库搜索路径/usr/lib
生成动态静态库文件的 头文件
vi my_lib.h
#ifndef __MY_LIB_H__ #define __MY_LIB_H__ int add(int x, int y); int sub(int x, int y); int mul(int x, int y); int div(int x, int y); void logger(const char *);//fprintf #endif
静态库实践
vi add.c
int add(int x, int y) { return x + y; }
vi sub.c
int sub(int x, int y) { return x + y; }
vi mul.c
int mul(int x, int y) { return x + y; }
vi div.c
int div(int x, int y) { return x + y; }
vi logger.c
#include <stdio.h> int logger(const char * str) { fprintf(stdout, "msg= %s\n", str); }
批量编译生成目标文件
gcc -c add.c sub.c mul.c div.c logger.c
得到
add.o sub.o mul.o div.o logger.o
ar归档:
ar cvr libmylib.a add.o sub.o mul.o div.o logger.o
测试静态库
vi test.c
#include "my_lib.h" int main(int argc, char **argv) { int c = add(2, 5); logger("hello world"); return 0; }
编译test.c:
gcc test.c -L. -lmylib -o test.out -Wall -O3 -std=c11
当前目录下链接 libmylib.so 或 libmylib.a
执行
[root@localhost ~]# ./test.out msg= hello world
查看静态库的目标文件
[root@localhost ~]# ar t libmylib.a add.o sub.o mul.o div.o logger.o
makefile自动构建
makefile只是一个构建 工具 的配置文件
原理:
根据源文件的最后修改时间, 检测哪些*.o目标文件需要重新编译;
根据makefile配置文件, 维护每个目标文件之间的依赖关系, 优先编译(或生成)被依赖的目标文件, 并根据执行命令去生成目标文件;
vi makefile
.PHONY: build test build2: libmylib.a libmylib.a: add.o sub.o mul.o div.o logger.o ar crv $@ add.o sub.o mul.o div.o logger.o add.o: add.c gcc -c add.c sub.o: sub.c gcc -c sub.c mul.o: mul.c gcc -c mul.c div.o: div.c gcc -c div.c logger.o: div.c gcc -c div.c test: a.out a.out: test.c libmylib.a gcc test.c -L. -lmylib -o a.out -Wall -O3 -std=c11
效果:
- 执行 make build2 会构建 libmylib.a
- 执行 make test 将会生成 链接了libmylib.a静态库的 test程序
先删除目标文件, 尝试用make build2 构建命令
[root@localhost ~]# rm -f add.o [root@localhost ~]# make build2 gcc -c add.c ar crv libmylib.a add.o sub.o mul.o div.o logger.o r - add.o r - sub.o r - mul.o r - div.o r - logger.o
删除静态库文件, 尝试用 make test 构建命令(自动编译被依赖的静态库)
[root@localhost ~]# rm -f libmylib.a [root@localhost ~]# make test ar crv libmylib.a add.o sub.o mul.o div.o logger.o a - add.o a - sub.o a - mul.o a - div.o a - logger.o gcc test.c -L. -lmylib -o a.out -Wall -O3 -std=c11
以上所述就是小编给大家介绍的《gcc: 基本操作(1)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 关于HBase Shell基本操作的表操作示例
- Elasticsearch索引的基本操作(8)-索引缓存、refresh、flush等操作
- MongoDB基本操作
- MySQL基本操作
- kubernetes基本操作
- Tensorflow基本操作
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。