linux下gcc的编译过程和功能,预处理,编译,汇编,链接,.c预处理为.i文件.s文件.o文件.exe文件

linux下gcc的编译过程和功能,预处理,编译,汇编,链接,.c预处理为.i文件.s文件.o文件.exe文件

2022找工作是学历、能力和运气的超强结合体,遇到寒冬,大厂不招人,此时学会c++的话,
我所知道的周边的会c++的同学,可手握10多个offer,随心所欲,而找啥算法岗的,基本gg
提示:系列c++学习的基础和高阶知识,用于公司生产实践中,实实在在的公司部署产品要用的,因为c++速度快,
而java和Python速度慢,自然往硬件里面部署算法啥的,都得用c++或者c,因此本科学的c很重要,后来的Python或者java就没有那么重要了,

c/c++系列文章:
【1】c++:c语言优缺点,visual studio2019如何新建项目,写hello world程序
【2】c/c++:gcc安装,gcc编译hello world文件,system函数调用系统命令,sleep函数


文章目录

  • linux下gcc的编译过程和功能,预处理,编译,汇编,链接,.c预处理为.i文件.s文件.o文件.exe文件
    • @[TOC](文章目录)
  • c/c++:linux下gcc的编译过程
  • 复习
  • gcc预处理做了什么?
  • gcc编译做了什么?
  • gcc汇编做了什么?
  • gcc链接做了什么?
  • 高度总结gcc四个步骤
  • 总结

c/c++:linux下gcc的编译过程

上一节课,我写过hello.c将其编译成可执行文件
linux下gcc的编译过程和功能,预处理,编译,汇编,链接,.c预处理为.i文件.s文件.o文件.exe文件_第1张图片
这是windows10下的过程

实际上,gcc的编译过程分别是4个步骤:
预处理
编译
汇编
连接

linux下gcc的编译过程和功能,预处理,编译,汇编,链接,.c预处理为.i文件.s文件.o文件.exe文件_第2张图片

预处理hello.i【参数-E】
编译hello.s【参数-S】
汇编hello.o【二进制机器代码文件,目标文件】【参数-C】
连接hello.exe【可执行文件】【无参数】
命令是:
linux下gcc的编译过程和功能,预处理,编译,汇编,链接,.c预处理为.i文件.s文件.o文件.exe文件_第3张图片
我们要把下面这个过程分解为四步
linux下gcc的编译过程和功能,预处理,编译,汇编,链接,.c预处理为.i文件.s文件.o文件.exe文件_第4张图片

gcc -E gan.c -o gan.i

linux下gcc的编译过程和功能,预处理,编译,汇编,链接,.c预处理为.i文件.s文件.o文件.exe文件_第5张图片

gcc -S gan.i -o gan.s

linux下gcc的编译过程和功能,预处理,编译,汇编,链接,.c预处理为.i文件.s文件.o文件.exe文件_第6张图片

gcc -C gan.s -o gan.o

linux下gcc的编译过程和功能,预处理,编译,汇编,链接,.c预处理为.i文件.s文件.o文件.exe文件_第7张图片
最后

gcc gan.o -o gan1

这步骤我没有成功
但是呢
就这四个步骤,你得熟悉

linux下gcc的编译过程和功能,预处理,编译,汇编,链接,.c预处理为.i文件.s文件.o文件.exe文件_第8张图片
预处理,编译,汇编,连接

复习

linux下gcc的编译过程和功能,预处理,编译,汇编,链接,.c预处理为.i文件.s文件.o文件.exe文件_第9张图片
系统头文件<>
""是自定义头文件

std标准库io是输入输出库
void最好是写,没有输入参数
可以不写,也是无参数
main必须有,程序中必须有,且只有一个,表示程序启的入口

//单行注释
/**/多行注释,块注释

f();函数调用完事冒号结束
{}函数主体,到此完事
return 0;程序正常结束

如果程序其他的还需要停止就需要调用系统函数system(“系统命令”);
windows.h文件别忘了哦,不会查百度
linux下gcc的编译过程和功能,预处理,编译,汇编,链接,.c预处理为.i文件.s文件.o文件.exe文件_第10张图片

整个代码文件需要是.c结束的文件哦
这是c
c++才是后面的东西

visual studio可以编译c文件
你当然也可以用txt文件写,改为.c后缀
用gcc编译即可

sleep是休眠函数,休眠多少ms吧

gcc预处理 -E 生成.i文件【预处理文件】
编译 -S 生成.s文件【汇编文件】
汇编 -c 生成目标文件.o【目标文件】
连接 无参数 生成可执行文件.exe【可执行文件】

反正四个步骤,你可以浓缩为一个步骤即可
好说
待会我们继续讲

gcc预处理做了什么?

linux下gcc的编译过程和功能,预处理,编译,汇编,链接,.c预处理为.i文件.s文件.o文件.exe文件_第11张图片

gcc预处理 -E 生成.i文件【预处理文件】

gcc -E hello.c -o hello.i

linux下gcc的编译过程和功能,预处理,编译,汇编,链接,.c预处理为.i文件.s文件.o文件.exe文件_第12张图片
参数别忘记了哦

.i是预处理文件哦

我们去看看.i文件,是啥东西?

预处理完成的是,头文件展开的工作
把头文件里面的内容,整体给你展开,方便咱们main函数后续使用头文件的各种函数
linux下gcc的编译过程和功能,预处理,编译,汇编,链接,.c预处理为.i文件.s文件.o文件.exe文件_第13张图片
你去看看hello.i文件,里面和hello.c的区别就是
hello.i的头文件一大堆,后面的代码和.c文件一样的
所以预处理处理的就是头文件。

不检查语法错误哦。

可以展开任意文件,但是错误不错误不知道。
在c语言中,.h就行,它不管,只看是否为.h文件即可。

咱们如果里面不写.h,而是写别的后缀文件
比如写一个hello.x
linux下gcc的编译过程和功能,预处理,编译,汇编,链接,.c预处理为.i文件.s文件.o文件.exe文件_第14张图片
你再预处理试试

D:\AWYY\leetcode\cplus\test>gcc -E hello.c -o hello2.i
hello.c:2:10: fatal error: xxx.x: No such file or directory
 #include "xxx.x"  //用户自定义的
          ^~~~~~~
compilation terminated.

gg了看到了吗

完成任务终止
失败了呗

但是这无大碍,因为我们没有xxx.x文件啊
它报错是因为没有这个文件
如果有呢?
咱们加上文件
linux下gcc的编译过程和功能,预处理,编译,汇编,链接,.c预处理为.i文件.s文件.o文件.exe文件_第15张图片
linux下gcc的编译过程和功能,预处理,编译,汇编,链接,.c预处理为.i文件.s文件.o文件.exe文件_第16张图片
你在编译试试

D:\AWYY\leetcode\cplus\test>gcc -E hello.c -o hello2.i

D:\AWYY\leetcode\cplus\test>

成功了
然后你看看hello2.i
里面是啥

# 1 "hello.c"
# 1 ""
# 1 ""
# 1 "hello.c"

# 1 "xxx.txt" 1
1111

2222

3333
# 3 "hello.c" 2

int main(void)
{
 printf("调用cmd命令");
 system("pause");


}

你看看,预处理干了什么?
不就是把这txt头文件内部的东西给导入到我这个文件里面了吗?

所以,懂了没,gcc预处理就是第一步,把头文件导入,我估计python的import就是这个意思也是要导入的头文件的,那些函数我们需要用。

但是不查错的哦。

它还做这些事情哦
linux下gcc的编译过程和功能,预处理,编译,汇编,链接,.c预处理为.i文件.s文件.o文件.exe文件_第17张图片
然后修改.c代码为

#include
//#include "xxx.txt"  //用户自定义的

#define PI 3.14  //宏定义常量PI

int main(void)
{
	printf("hello world\n");
	printf("d%", PI);  //打印PI
}

你再编译时时

D:\AWYY\leetcode\cplus\test>gcc -E hello.c -o hello2.i

D:\AWYY\leetcode\cplus\test>

看看.i文件什么情况



# 6 "hello.c"
int main(void)
{
 printf("hello world\n");
 printf("d%", 3.14);
}

发现了啥?
PI变3.14了
所以说预处理,直接将宏定义的常量直接用常量替换了。
即宏名替换为宏值

另外,你看看,那些注释跑哪里去了????
没了
相当于将注释直接废掉,用空字符替代了
这也是预处理的功能之一
美滋滋。

还有,展开条件编译,怎么说呢

#include
//#include "xxx.txt"  //用户自定义的

#define PI 3.14  //宏定义常量PI

int main(void)
{
	printf("hello world\n");
	printf("d%", PI);  //打印PI

#ifdef PI
printf(1);//如果上面定义了PI,则打印1
#endif
}

编译试试

D:\AWYY\leetcode\cplus\test>gcc -E hello.c -o hello2.i

D:\AWYY\leetcode\cplus\test>
# 6 "hello.c"
int main(void)
{
 printf("hello world\n");
 printf("d%", 3.14);


printf(1);

}

因为PI定义了,所以一定要打印
条件编译

这种就预处理也要搞的事情。

懂?

还有一些预处理要干的事情,现在先了解到现在就可
linux下gcc的编译过程和功能,预处理,编译,汇编,链接,.c预处理为.i文件.s文件.o文件.exe文件_第18张图片

gcc编译做了什么?

编译 -S 生成.s文件【汇编文件】
linux下gcc的编译过程和功能,预处理,编译,汇编,链接,.c预处理为.i文件.s文件.o文件.exe文件_第19张图片
咱们直接将hello.i
用命令编译

D:\AWYY\leetcode\cplus\test>gcc -S hello.i -o hello.s
hello.c: In function 'main':
hello.c:7:2: warning: implicit declaration of function 'system' [-Wimplicit-function-declaration]
 {
  ^

编译之后的文件叫做汇编文件
汇编语言写的文件,顾名思义

	.file	"hello.c"
	.text
	.def	__main;	.scl	2;	.type	32;	.endef
	.section .rdata,"dr"
.LC0:
	.ascii "\265\367\323\303cmd\303\374\301\356\0"
.LC1:
	.ascii "pause\0"
	.text
	.globl	main
	.def	main;	.scl	2;	.type	32;	.endef
	.seh_proc	main
main:
	pushq	%rbp
	.seh_pushreg	%rbp
	movq	%rsp, %rbp
	.seh_setframe	%rbp, 0
	subq	$32, %rsp
	.seh_stackalloc	32
	.seh_endprologue
	call	__main
	leaq	.LC0(%rip), %rcx
	call	printf
	leaq	.LC1(%rip), %rcx
	call	system
	movl	$0, %eax
	addq	$32, %rsp
	popq	%rbp
	ret
	.seh_endproc
	.ident	"GCC: (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 7.3.0"
	.def	printf;	.scl	2;	.type	32;	.endef
	.def	system;	.scl	2;	.type	32;	.endef

这些都是汇编指令

汇编是连接c语言和机器指令的语言

所以:gcc编译的过程,就是将c语言汇编为汇编语言
如果代码有错误,无法汇编成功
生成失败就会删除那个代码

汇编的功能之一就是逐行检查代码的正确性。这是整个编译过程中最耗时的。
linux下gcc的编译过程和功能,预处理,编译,汇编,链接,.c预处理为.i文件.s文件.o文件.exe文件_第20张图片

gcc汇编做了什么?

汇编 -c 生成目标文件.o【目标文件】
linux下gcc的编译过程和功能,预处理,编译,汇编,链接,.c预处理为.i文件.s文件.o文件.exe文件_第21张图片
现在的话,我们就要汇编了
将s文件,生成为目标文件.o

D:\AWYY\leetcode\cplus\test>gcc -c hello.s -o hello.o

注意,参数是c哦
不是别的大C啥的,否则汇编不成功

txt打开是乱码了
实际上文件里面是二进制的机器指令
linux下gcc的编译过程和功能,预处理,编译,汇编,链接,.c预处理为.i文件.s文件.o文件.exe文件_第22张图片
反正你要知道,这是目标文件即可
将汇编指令翻译为机器可以识别的二进制代码

因为机器才能认识那些指令,我们人是记不住的
因为记不住,才造出来汇编语言,汇编也麻烦,所以后来发明了c语言
c语言大家嫌麻烦,所以后来出现了c++和python,java等等等等

懂吧???

所以,高级语言在运行之前,需要把这些工作做好,然后一步步往下转
翻译为机器语言

gcc链接做了什么?

连接 无参数 生成可执行文件.exe【可执行文件】
linux下gcc的编译过程和功能,预处理,编译,汇编,链接,.c预处理为.i文件.s文件.o文件.exe文件_第23张图片
okay,这波我们就可以去生成可执行文件了

D:\AWYY\leetcode\cplus\test>gcc hello.o -o hello

链接的目的:
数据段合并
数据地址回填
库引用
linux下gcc的编译过程和功能,预处理,编译,汇编,链接,.c预处理为.i文件.s文件.o文件.exe文件_第24张图片

linux下gcc的编译过程和功能,预处理,编译,汇编,链接,.c预处理为.i文件.s文件.o文件.exe文件_第25张图片
你在cmd里面运行hello.exe就可以运行了

D:\AWYY\leetcode\cplus\test>hello.exe
调用cmd命令请按任意键继续. . .

D:\AWYY\leetcode\cplus\test>

懂了吧
gcc四个步骤,就这么来的

当然你可以一步到位

D:\AWYY\leetcode\cplus\test>gcc hello.c -o hello2

D:\AWYY\leetcode\cplus\test>hello2.exe
hello world

D:\AWYY\leetcode\cplus\test>

但是你要知道gcc的4个步骤,一步一步都是怎么来的,懂?

高度总结gcc四个步骤

linux下gcc的编译过程和功能,预处理,编译,汇编,链接,.c预处理为.i文件.s文件.o文件.exe文件_第26张图片
如果你用hello.c
直接用参数S,它就会跳过预处理阶段,直接生成汇编文件
直接用参数c,它就会跳过预处理和编译阶段,直接生成目标文件
直接不用参数,它就会跳过预处理,编译和链接,直接生成可执行文件

懂?

linux下gcc的编译过程和功能,预处理,编译,汇编,链接,.c预处理为.i文件.s文件.o文件.exe文件_第27张图片


总结

提示:重要经验:

1)
2)学好c++,即使经济寒冬,手握10个大厂offer绝对不是问题!
3)笔试求AC,可以不考虑空间复杂度,但是面试既要考虑时间复杂度最优,也要考虑空间复杂度最优。

你可能感兴趣的:(c++,c语言,gcc,编译汇编链接,c++,可执行文件)