编译过程,gcc/g++,make命令

gcc 编译程序的四个阶段

gcc指令的一般格式为:gcc [选项] 要编译的文件 [选项] [目标文件]
其中,目标文件可缺省,gcc 默认生成可执行的文件名为:编译文件.out

1)预处理(Pre-processing)
在该阶段,编译器将C源代码中的包含的头文件如stdio.h编译进来。可以使用gcc的选项”-E”进行查看,该选项使得 gcc 在预处理结束后停止编译过程,从而生成预处理之后的文件。
选项:-E
用法:gcc -E main.c -o main.i
作用:将 main.c 预处理输出 main.i 文件

2)编译阶段(Compiling)
第二步进行的是编译阶段,在这个阶段中,gcc 首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,gcc 把代码翻译成汇编语言。可以使用”-S”选项来进行查看,该选项使得 gcc 在编译结束后停止编译过程,从而生成汇编代码。
选项: -S
用法:gcc –S main.i –o main.s
作用:将预处理输出文件 main.i 汇编成 main.s 文件

3)汇编阶段(Assembling)
汇编阶段是把编译阶段生成的”.s”文件转成二进制目标代码。可使用选项”-c”来进行查看,该选项使得 gcc 在汇编结束后停止编译过程从而生成二进制目标代码了。
选项:-c
用法:gcc –c main.s –o main.o
作用:将汇编输出文件 main.s 编译输出 main.o 文件

4)链接阶段(Link)
在成功编译之后,就进入了链接阶段,此阶段会生成最终的可执行文件。
读者可以重新查看这个小程序,在这个程序中并没有定义”printf”的函数实现,且在预编译中包含进的”stdio.h”中也只有该函数的声明,而没有定义函数的实现,那么,是在哪里实现”printf”函数的呢?最后的答案是:系统把这些函数实现都被做到名为libc.so.6的库文件中去了,在没有特别指定时,gcc会到系统默认的搜索路径”/usr/lib”下进行查找,也就是链接到libc.so.6库函数中去,这样就能实现函 数”printf”了,而这也就是链接的作用。函数库一般分为静态库和动态库两种。静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件 了。其后缀名一般为”.a”。动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以 节省系统的开销。动态库一般后缀名为”.so”,如前面所述的libc.so.6就是动态库。gcc在编译时默认使用动态库。完成了链接之后,gcc就可以生成可执行文件
无选项链接
用法:gcc main.o –o main.out
作用:将编译输出文件 main.o 链接成最终可执行文件main

gcc/g++ 常用参数

https://www.cnblogs.com/MyOnlyBook/p/9526889.html

  • -Wall, -W, -w:显示警告信息;显示可能会引起错误的警告信息;屏蔽警告信息
  • -g, -g2, -g0, -g3:生成不同级别的调试信息,其中 -g0 表示不生成调试信息
  • -O, -O1, -O2, -O3, -Os:优化代码
  • -lm,链接数学库;-pthread,链接线程库
  • -E,将源代码变成预处理之后的代码
  • -S,将源代码变成汇编代码
  • -c,将源代码变成机器代码
多文件调用问题
  • 第一种

不使用 .h 文件,而是将代码写在 .c 文件中,其他文件要调用该 .c 文件的函数或变量时直接包含 .c 文件。当有多个文件同时包含同一个 .c 文件时,会导致重定义问题,编译不通过。

  • 第二种

同样也是不使用 .h 文件而是将代码写在 .c 文件中,但不同的是,当其他文件要调用该 .c 文件的函数或变量时,在该调用文件中使用 extern 进行声明(这是因为全局变量和全局函数可以被外部引用),而对于函数则可以不必使用 extern 声明。对于每个调用文件,都需要显示声明所用到的全局变量和全局函数,导致代码的冗余。

  • 第三种

为每一个模块都编写一个 .c 文件,一个 .h 文件。.c 源文件中放:函数的定义,全局变量的定义,全局结构体变量的定义。.h 头文件中放:函数的声明,全局变量的声明(extern),全局结构体的定义,全局结构体变量的声明。调用文件(main.c)文件中:包含.h头文件即可,不用声明任何东西。

假设有如下文件:bar.c, bar.h, foo.c, foo.h, common.h, main.c。其中 bar.h 定义 bar.c 的接口,foo.h 定义 foo.c 的接口,然后给main.c 使用,而 common.h 包含所有文件都要用到的通用定义(bar.h 和 foo.h 中包含 common.h),为了生成名为 main 的可执行文件,编译命令可写为,

gcc -c bar.c -o bar.o
gcc -c foo.c -o foo.o
gcc -c main.c -o main.o
gcc main.o bar.o foo.o -o main
make与makefile初探

https://zhuanlan.zhihu.com/p/29910215

https://www.cnblogs.com/paul-617/p/15501875.html

makefile 中写明了程序的依赖关系,make 命令结合该 makefile 文件将源代码经过预处理编译汇编链接从而生成可执行文件。

同上面同样的文件依赖关系,写成makefile文件,如下所示,

.PHONY: clean
CC = gcc
CFLAGS = -Wall -c

main: foo.o woo.o main.o
	$(CC) $^ -o $@
%.o: %.c
	$(CC) $(CFLAGS) $^ -o $@
foo.o: foo.c foo.h common.h
bar.o: bar.c bar.h common.h
main.o: main.c bar.h foo.h common.h
clean:
	rm *.o main

当然还可以继续化简,比如gnumake其实是默认定义了一组rule的,上面这个整个都可以不写,就这样就可以了,

LDLIBS=-lpthead
CFLAGS+=-MMD -MP
SRC=$(wildcard *.c)
OBJ=$(SRC:.c=.o)
DEP=$(SRC:.c=.d)
-include $(DEP)
app.executable: $(OBJ)

这里其实没有定义.o到.c的依赖,但gnumake默认如果.c存在,.o就依赖对应的.c,而.o到.c的rule,是通过宏默认定义的。你只要修改CC,LDLIBS这类的宏,就能解决大部分问题了。所以又省掉了一组定义,这就可以写得很短。CFLAGS中增加的参数可以为xxx.c产生xxx.d文件,里面就是那个依赖关系,然后我用-include包含这些依赖关系。这样就不再需要手工来写每个依赖了。为了解决这个问题,你已经看见了,include前面又增加了一个语法。那个减号表示允许被包含的文件不存在。

cmake

make 工具通过调用 makefile 文件中的命令便可以对大型程序进行编译,而 makefile 文件中就包含了调用 gcc 去编译多个源文件的命令。但是如果程序是跨平台的,换个平台之后 makefile 又要重新修改,会很麻烦,所以出现了 cmake 工具,通过 cmake 我们可以快速创建出不同平台的 makefile 文件。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值