高级软件工程师之路-汇编和可执行文件

来源:互联网 发布:单片机led流水灯程序 编辑:程序博客网 时间:2024/05/02 01:59

汇编是助记符,与机器码一一对应


如何生成一个可执行文件


  • 1.编译

    编译过程又可以分成两个阶段:编译和汇编。

    编译是指编译器读取源程序(字符流),对之进行词法和语法的分析,将高级语言指令转换为功能等效的汇编代码。

    源代码 -> 编译器进行编译 -> 汇编代码

    汇编实际上指汇编器(as)把汇编语言代码翻译成目标机器指令的过程。

    汇编码是不可执行的。
    在编译器执行完成后,会生成.obj文件(Windows)或.o文件(Linux或Unix),里面存放的就是与将源码翻译后的汇编码。

  • 2.链接

    链接器主要是将有关的目标文件彼此相链接生成可加载、可执行的目标文件。链接器的核心就做就是用符号表解析和重定位。

    链接器会讲编译出来的.obj文件进行链接操作,生成可执行文件。

编译器


  • 编译器会优化代码

我们用VS调试程序的时候推荐使用Debug模式,Debug版会对每一句代码都生成相应的汇编码,进行完全编译。而还有一种Release版(发行版)则会进行代码优化。

编译器的本质,其实就是将一些复杂逻辑分解成简单逻辑。

程序运行在物理内存中



  • 内存分配

一个由C/C++编译的程序占用的内存分为以下几个部分

原因:
我们在用能操作内存的语言(如:c/c++)写程序的过程中,有意无意的过程中,可能会将指针指向代码区或者常量区,修改了代码区或者常量区中的内容,会导致程序的崩溃。所以程序内存分为四块,并且代码区和常量区设为只读。

  • 栈(Stack):
    由编译器自动分配释放,存放函数返回地址,函数的参数值,局部变量的值等,操作方式类似于数据结构中的栈。
    程序在开始运行时候,默认会生成1024KB大小的一块内存。
    程序在运行过程中,有时候会遇到栈溢出。
    如:

    int main()
    {
    char str[1024 * 1024] = {0};
    return 0;
    }

    这段代码的运行就会导致Stack overflow错误!
    怎么解决?
    答:从堆上申请内存空间。

    临时变量为何放在栈中?
    答:对临时变量的分配内存和删除是要时间的,保存在栈中,用ESP和EBP两个寄存器,一个指向栈顶,一个指向栈底,通过栈顶的上下移动和push、pop操作来定义变量和删除变量,省去了删除操作,节省了时间。

  • 堆(Heap):
    由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配方式类似于链表。

  • 全局区(静态区Static):
    全局变量和静态变量的存储是放在一起的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。
  • 代码区:
    顾名思义,存放代码的。

  • 文字常量区:
    常量字符串就存放在这片区域,程序结束后系统释放。
  • 一个例子


    代码 --> 1 //main.cpp  #include <cstdio>  int a=0;    //全局初始化区  char *p1;   //全局未初始化区  int main()  {   int b;                //栈   char s[]="abc";       //栈   char *p2;             //栈   char *p3="123456";    //123456\0在常量区,p3在栈上   static int c=0//全局(静态)初始化区   p1 = (char*)malloc(10);   p2 = (char*)malloc(20);   //分配得来得10和20字节的区域就在堆区。   return 0;  }

    常用寄存器


    • EIP 程序计数器
    • EFL 标志寄存器
    • EAX 累加寄存器
    • EDI 源寄存器
    • ESI 基址寄存器
    • EBP 栈底
    • ESP 栈顶
    0 0