汇编和可执行文件
1.编译:得到obj文件 : 源代码----->机器码(约等于汇编)
=>通过编译器实现这个过程
=>编译器:将一行行的c/c++代码翻译成与之一一对应的汇编代码,将复杂逻辑分解成简单逻辑逻辑(c/c++中有很多种的语句,但是汇编中只有三种语句(见下方))
=>编译器是稳定可靠的,但是也有例外的情况
=>相同的代码,编译的时候可能会生成不一样的代码:因为编译器会进行优化
=>当代码中出现废代码的时候,现在的编译器较为强大,会进行优化而不翻译成汇编代码
显然,同一段代码,在不同的条件下编译,所得到的汇编代码是不相同的,Debug下,会对每句代码都生成汇编代码如输入无意义的int num=10;Debug依旧有生成相应的汇编代码(调试的时候,建议用Debug,它会对每句代码生成对应的汇编,不会省略);Release下,同样输入无意义代码,不会生成汇编(发布版,要求快速、精悍)。这同时也解释了,为什么通过Release得到的exe文件会小于Debug,如下图:
有一个疑点:为什么这两个应用程序的大小和所占用空间不相等呢?
=>这是因为“簇”的存在(可看第四课笔记)
软件通过调用硬件起作用,当我们写完printf("hello")这一行代码时,能够得到这一行代码所对应的汇编代码(通过编译实现),但是如何将“helllo”显示在显示屏上?这就需要我们的CPU进行对显卡的调用,而我们并没有写这些代码。是由操作系统给我们提供了这一功能。但是,每一个操作系统都有自己的独特的API,我们要将我们所写的代码符合该操作系统的编写规范,才能够调用CPU,进而调用硬件。
=>由汇编代码得到符合该操作系统编写规范的可执行文件的过程,称之为链接(告诉该系统,我想要干什么的过程)
2.链接:加上PE结构,得到可执行文件
=>会生成与当前平台对应的格式
代码到可执行文件的类比:
源代码:生猪
编译:将生猪宰杀干净
链接:把肉进行再处理,加入各种调料,做成一道可以吃的菜
=>栈区:存储临时变量、参数(默认生成1024KB供本程序使用)=>栈溢出:如果数组的大小超出了最大的长度,那么就会发生栈溢出:如char arr[1024*1024]=>解决:把这个数组放在堆上:char *arr=new char[1024*1024]=>栈内存的使用与释放:划出一整段内存作为栈,用ebp存储当前栈底的地址,esp存储当前栈顶的地址(ebp~esp之间的空间即为当前运行函数所使用的栈空间),当要运行另一个函数的时候,会有保存当前ebp位置、发生ebp与esp置同、esp上游等动作。(详情请看这这节课的作业)
5.知识补充:
1)汇编指令 mov 目标,源
2)内存分区:栈、堆、代码、常量
=>这样分区更为安全:内存里的数据都是0和1,对于能够操作内存的语言,如果程序员把指针指向代码段,而且不小心修改了,那么程序就挂了
=>把所有的代码放在一个区段(只读不写),如果指针指向了代码段,那么就会报错,达到保护的作用
6作业:写一个“I Love Mark”,转到反汇编记录其中的各个信息
=>总结:函数是如何进行调用、传参、如何清理里面的临时变量的
EAX:累加寄存器
EBX:基址寄存器
ECX:计数器
EDX:存放放整数除法产生的余数
ESI: DS:ESI指向源串
EDI: DS:EDI指向目标串===>以上都用来做通用寄存器
EIP:程序计数器
ESP:栈顶
EBP:栈底
EFL:标志寄存器