精华内容
下载资源
问答
  • C++的编译过程及原理

    万次阅读 多人点赞 2018-09-27 20:25:03
    目录 文章目录目录C和C++的编译 本次内容是关于编译过程的,内容如下: C和C++的编译 C和C++的编译

    目录

    本次内容是关于c++编译过程的,内容如下:

    • C和C++的程序结构
    • 预编译
    • 编译过程及链接


    C和C++程序结构

    我们来看一个基本程序,由animal.h ,animal.cpp,human.h,human.cpp ,main.cpp等5个文件组成:

    ------------------------------ 
    animal.h 头文件 这样写:
    ------------------------------
    #ifndef  _animal_H 
    #define _animal_H 
    
    #include "iostream.h"
    
    class  animal
    {
    	public:
    	int move;
    	void out_put();
    }
    
    void animal :: out_put()
    {
    	cout<<"move="<<move<<endl;
    }
    
    extern animal Dongwu;  //加了extern 关键字声明定义在其他文件中
    
    void show();//函数声明
    
    
     #endif
    
    ------------------------------ 
    animal.cpp 文件 这样写:
    ------------------------------
    #include "animal.h"
    
    
    animal Dongwu;  //定义animal对象  Dongwu
    void show()
    {
    	cout<<"show move="<<Dongwu.move<<endl;
    }
    
    
    ------------------------------ 
    human.h 头文件 这样写:
    ------------------------------
    #ifndef  _human_H 
    #define _human_H 
    
    #include "animal.h"   //human类 要从animal类 中继承
    #include "iostream.h"
    
    class  human: public animal
    {
    	public:
    	int thought;
    }
    
    void showme();//函数声明
    
     #endif
    
    ------------------------------ 
    animal.cpp 文件 这样写:
    ------------------------------
    #include "animal.h"
    human me;  //定义human对象  me
    void showme()
    {
    	cout<<"thought="<<human.move<<endl;
    }
    
    
    
    
    ```c
    ------------------------------ 
    main .cpp  主函数文件写法:
    ------------------------------
    #include "human.h"
    #include <iostream.h>
    void main()
    {
    	animal.out_put();
    	show();
    	showme();
    }
    
    

    我们发现,但凡是声明一般都放在了头文件中,比如animal类的声明以及show();等函数的声明。
    但是为什么这么做呢?我们接下来将会说明。


    预编译

    我们发现了头文件中有一些带#开头的关键字,如:#define,#ifndef,#endif,等等。
    这阶段是预处理阶段,比如说·#define m 5,那么在该阶段会将程序中的m全部替换成5
    想必对于#define,大家都熟悉,接下来我们说说条件编译的关键字:

    条件编译指令:#ifdef,#ifndef,#else,#elif,#endif等。
    我们通常这样使用:

    #ifndef  xxx
    #define  xxx
    		..............
    		..............
    #else
    		.............
    		......
    #endif
    

    如果这是程序中的条件选择,想必大家就很好理解了:

    if ( xxx==Null)
    {   
    	 xxx=true;
    		..............
    		..............
    }
    else
    {		.............
    		......
    }
    
    

    只不过,程序中的条件选择是在程序运行的时候才被执行的,而条件编译是在预编译时执行的条件选择。
    那么条件编译有什么作用呢?

    #ifndef  Linux
    	linux平台下运行的函数	
    #else
    	#ifndef  windows
    	Windows平台下运行的函数
    	#endif         
    #endif
    

    我们可以看到,这样可以兼容不同的平台,想在linux平台下运行,只要在条件编译前添加#define Linux就好了
    另外,也可以通过这种方式来选择不接入某些不需要用的模块,提高编译速度

    除此之外,还有一个作用,举个例子,头文件中这样写:

    #ifndef  _human_H 
    #define _human_H 
    
    #include "animal.h"   //human类 要从animal类 中继承
    #include "iostream.h"
    
    class  human: public animal
    {
    	public:
    	int thought;
    }
    
    void showme();//函数声明
    #endif
    

    我们可以把每一个文件看做一个函数,用函数来理解过程就容易多了:

    void human()
    {
    	static _human_H=false;
    
    	if(_human_H==false)  // #ifndef  _human_H 
    	{
    		 _human_H =true; // #define _human_H 
    		animal();        //  #include "animal.h"   
    		iostream();     //  #include "iostream.h"
    		......待编译内容
    		......待编译内容
    	}    //  #endif
    }
    

    假设每一个头文件都是一个函数,每个文件中每#include一个头文件的时候,就相当于调用一次那个函数。
    用了条件编译,我们可以保证,每个头文件只调用一次,但是为什么要只调用一次?
    假设两个函数这样:

    void animal()
    {
    static _animal_H=false;
    	
    		human();
    }
    void human()
    {
    	static _human_H=false;
    		
    		animal();        //  #include "animal.h"   
    }
    

    你猜会发生什么,是不是会死循环?两个函数相互调用直到天荒地老。并且调用多次编译器会显示重复定义的错误。
    如果加了条件选择:

    void animal()
    {
    static _animal_H=false;
    if(_animal_H==false)
    	{
    	_animal_H=ture;
    		human();
    	}
    }
    
    void human()
    {
    	static _human_H=false;
    	if(_human_H==false)
    	{
    		_human_H=true;
    	
    		animal();        //  #include "animal.h"  
    	} 
    }
    

    这样我们能保证每个函数只调用一次,而不会相互一直调用
    也正是因为这样,所以我们才能放心大胆的随便 #include:

    ------------------------------ 
    animal.h 头文件 这样写:
    ------------------------------
    #ifndef  _animal_H 
    #define _animal_H 
    
    #include "human.h" 
    
    .......
    
     #endif
    
    ------------------------------ 
    human.h 头文件 这样写:
    ------------------------------
    #ifndef  _human_H 
    #define _human_H 
    
    #include "animal.h"   //human类 要从animal类 中继承
    
    .........
    
     #endif
    

    我之所以用函数的运行来举例说明,那是因为每个#include "animal.h"实际上就是用animal.h文件的内容将其替换。#define 的作用是替换单个符号,而#include的作用是将这个#include用其include的头文件进行替换。如果将文件抽象为函数,那么本质上并没有什么太大区别。

    • 接下来说明一下预编译过程,以及为什么#include一般放在文件开头:

    我们刚开始学c++的时候,程序都是很小的,所以都写在同一个文件中:

    #include "iostream.h"
    
    class  animal
    {
    	public:
    	int move;
    	void out_put();
    }
    void animal :: out_put()
    {
    	cout<<"move="<<move<<endl;
    }
    animal Dongwu;  //定义animal对象  Dongwu
    void show(); //函数声明
    
    
    void main()
    {
    	animal.out_put();
    	show();
    }
    
    void show()
    {
    	cout<<"show move="<<Dongwu.move<<endl;
    }
    

    但是随着文件越来越大,这种方式是不现实的,看上去就特别乱,我们知道,调用一个函数之前,必须先能找到他的声明,所以我们将各种要调用的函数声明打包成头文件,并将其添加到main函数之前,在预编译过程中,#include将会被其文件内容替换,从而实现将函数声明放在main之前,为main函数中调用打下基础,这就是为什么#include为什么被放在cpp文件的开始部分。

    • 另外这里对#include“animal.h”#include <animal.h>进行说明一下区别
      .
      <>和“”表示编译器搜索头文件的顺序不同:

    • <>表示从系统目录下开始搜索,然后再搜索PATH环境变量所列出的目录,不搜索当前目录

    • ""表示先从当前目录搜索,然后是系统目录和PATH环境变量所列出的目录下搜索
      .
      所以如果我们知道头文件在系统目录或者环境变量目录下时,可以用<>来加快搜索速度。

    编译过程及链接

    当我们点击编译按钮时,通常会出现如下提示:

    Compiling...
    
    animal.cpp
    human.cpp
    
    ...
    Linking...
    
    main.exe - 0 error(s), 0 waring(s)
    

    这段文字输出事实上已经说明了编译的步骤了
    编译器先对工程中三个源文件main.cppanimal.cpphuman.cpp进行单独编译 (Compiling...

    在编译时,由预处理器对预处理指令(#include、#define…)进行处理,在内存中输出翻译单元(就是将include等在源文件上替换了以后产生的临时文件)。
    编译器接受临时文件,将其翻译成包含机器语言指令的三个目标文件(main.objanimal.objhuman.obj
    接下去就是链接过程(Linking...),连接器将目标文件和你用到的相关库文件一起链接形成main.exe。
    到此,编译也就结束了。

    注意:在编译过程中头文件不参与编译,预编译时进行各种替换以后,头文件就完成了其光荣使命,不再具有任何作用
    最后以一张图来结束本次内容:
    在这里插入图片描述

    结语

    本次介绍了C++的编译过程及原理

    展开全文
  • Android源码编译过程及刷机过程详解

    万次阅读 2020-09-01 13:55:35
    本文将详细介绍Android源码编译过程,以及编译完成后如何进行刷机。

    Android编译环境进行初始化


    我们完成AOSP源码下载之后,就可以准备源码进行编译了。但编译之前,首先要对编译环境进行初始化工作。

    在这个过程中,主要是指定编译的类型和目标设备的型号。

    Android的编译类型主要有eng、userdebug和user三种,而支持的目标设备型号则是不确定的,它们由当前的源码配置情况所决定。为了确定源码支持的所有目标设备型号,Android编译系统在初始化的过程中,需要在特定的目录中加载特定的配置文件。

    清除缓存(如果需要的话)

    如果之前执行过编译工作,或者编译出错了,可以执行清除命令:

    $ make clobber
    

    执行envsetup.sh,配置编译环境

    打开一个终端(bash),cd到源码根目录,并且将build/envsetup.sh加载到该终端中:

    $ source build/envsetup.sh
    或
    $ . ./build/envsetup.sh 
    
    including device/generic/car/vendorsetup.sh
    including device/generic/mini-emulator-arm64/vendorsetup.sh
    including device/generic/mini-emulator-armv7-a-neon/vendorsetup.sh
    including device/generic/mini-emulator-mips/vendorsetup.sh
    including device/generic/mini-emulator-mips64/vendorsetup.sh
    including device/generic/mini-emulator-x86/vendorsetup.sh
    including device/generic/mini-emulator-x86_64/vendorsetup.sh
    including device/generic/uml/vendorsetup.sh
    including device/google/crosshatch/vendorsetup.sh
    including device/google/cuttlefish/vendorsetup.sh
    including device/google/marlin/vendorsetup.sh
    including device/google/muskie/vendorsetup.sh
    including device/google/taimen/vendorsetup.sh
    including device/linaro/hikey/vendorsetup.sh
    including sdk/bash_completion/adb.bash
    

    我们来看命令的输出,文件build/envsetup.sh在加载的过程中又会加载一些其他文件。

    • 在device目录中加载那些名称为vendorsetup.sh的文件。

    • 在sdk/bash_completion目录下的adb.bash文件也会加载到当前终端来。

      它是用来实现adb命令的bash completion功能的。也就是说,加载了该文件之后,我们在运行adb相关的命令的时候,通过按tab键就可以帮助我们自动完成命令的输入。

    执行lunch命令

    执行命令lunch,选择编译的目标:

    $ lunch
    
    You're building on Darwin
    
    Lunch menu... pick a combo:
         1. aosp_arm-eng
         2. aosp_arm64-eng
         3. aosp_mips-eng
         4. aosp_mips64-eng
         5. aosp_x86-eng
         6. aosp_x86_64-eng
         7. aosp_car_arm-userdebug
         8. aosp_car_arm64-userdebug
         9. aosp_car_x86-userdebug
         10. aosp_car_x86_64-userdebug
         11. mini_emulator_arm64-userdebug
         12. m_e_arm-userdebug
         13. m_e_mips-userdebug
         14. m_e_mips64-eng
         15. mini_emulator_x86-userdebug
         16. mini_emulator_x86_64-userdebug
         17. uml-userdebug
         18. aosp_crosshatch-userdebug
         19. aosp_blueline-userdebug
         20. aosp_cf_x86_auto-userdebug
         21. aosp_cf_x86_phone-userdebug
         22. aosp_cf_x86_tablet-userdebug
         23. aosp_cf_x86_tablet_3g-userdebug
         24. aosp_cf_x86_tv-userdebug
         25. aosp_cf_x86_wear-userdebug
         26. aosp_cf_x86_64_auto-userdebug
         27. aosp_cf_x86_64_phone-userdebug
         28. aosp_cf_x86_64_tablet-userdebug
         29. aosp_cf_x86_64_tablet_3g-userdebug
         30. aosp_cf_x86_64_tv-userdebug
         31. aosp_cf_x86_64_wear-userdebug
         32. cf_x86_auto-userdebug
         33. cf_x86_phone-userdebug
         34. cf_x86_tablet-userdebug
         35. cf_x86_tablet_3g-userdebug
         36. cf_x86_tv-userdebug
         37. cf_x86_wear-userdebug
         38. cf_x86_64_phone-userdebug
         39. cf_x86_64_tablet-userdebug
         40. cf_x86_64_tablet_3g-userdebug
         41. cf_x86_64_tv-userdebug
         42. cf_x86_64_wear-userdebug
         43. aosp_marlin-userdebug
         44. aosp_marlin_svelte-userdebug
         45. aosp_sailfish-userdebug
         46. aosp_walleye-userdebug
         47. aosp_walleye_test-userdebug
         48. aosp_taimen-userdebug
         49. hikey-userdebug
         50. hikey64_only-userdebug
         51. hikey960-userdebug
    

    lunch命令输出了一个Lunch菜单,该菜单列出了当前Android源码支持的所有设备型号及其编译类型。

    编译的类型:

    • user: limited access; suited for production(有限的访问权限,一般用于发布版)。
    • eng:具有开发配置,并且有额外的调试工具(注:工程师模式engineer)。
    • userdebug: 这个和user类似,但是可以获取root权限,并且能够调试。

    当我们选定了一个Lunch菜单项序号(1-51)之后,按回车键,就可以完成Android编译环境的初始化过程。

    注:lunch命令也可以直接这么用 $ lunch aosp_arm-eng

    编译环境初始化结果

    Android编译环境初始化完成之后,获得了以下三样东西:

    1. 将vendor和device目录下的vendorsetup.sh文件加载到了当前终端。
    2. 新增了lunch、m、mm和mmm等命令。
    3. 通过执行lunch命令设置好了TARGET_PRODUCT、TARGET_BUILD_VARIANT、TARGET_BUILD_TYPE和TARGET_BUILD_APPS等环境变量。

    AOSP源码编译


    对整个系统进行编译

    使用make命令开始整个系统的编译:

    make -j8
    

    这里的-j参数后面的数字是编译需要的线程数,建议电脑的CPU数量的1~2倍来设置。

    然后就是漫长的等待过程了……

    分模块编译

    我们也可以用m/mm/mmm/make命令编译源代码。

    当然,这要求每一个模块都有一个Android.mk文件。Android.mk实际上是一个Makefile脚本,用来描述模块编译信息。Android编译系统通过整合Android.mk文件完成编译过程。

    m、mm和mmm命令也分别是由定义在build/envsetup.sh文件中的函数m、mm和mmm提供的,而这三个函数又都是通过make命令来对源代码进行编译的。

    m/mm/mmm使用简介

    • m: Makes from the top of the tree.
    • mm: Builds all of the modules in the current directory, but not their dependencies.
    • mmm: Builds all of the modules in the supplied directories, but not their dependencies.
      To limit the modules being built use the syntax: mmm dir/:target1,target2.
    • mma: Builds all of the modules in the current directory, and their dependencies.
    • mmma: Builds all of the modules in the supplied directories, and their dependencies.

    事实上,命令m就是对make命令的简单封装,并且是用来对整个Android源代码进行编译,而命令mm和mmm都是通过make命令来对Android源码中的指定模块进行编译。

    m的实现:

    function m()
    {
        local T=$(gettop)
        if [ "$T" ]; then
            _wrap_build $T/build/soong/soong_ui.bash --make-mode $@
        else
            echo "Couldn't locate the top of the tree.  Try setting TOP."
            return 1
        fi
    }
    

    当在Android源码中定义的各个模块都编译好之后,我们还需要将编译得到的文件打包成相应的镜像文件,例如system.img、boot.img和recorvery.img等,这样我们才可以将这些镜像烧录到目标设备去运行。

    安装系统


    系统编译完成之后,我们可以通过使用模拟器来运行,或者使用真机进行刷机。

    模拟机启动

    $ source build/envsetup.sh
    $ lunch //(选择刚才设置的目标版本,例如如果之前我们选择1,那就是aosp_arm-eng)
    $ emulator //模拟器启动
    

    刷真机

    1. 进入fastboot模式
    $ adb reboot bootloader
    
    1. 把img文件刷进去
    $ fastboot flashall -w  //这个 -w 是为了wipes the /data partition擦除/data分区
    

    **PS:更多精彩内容,请查看 --> 《AOSP 专栏》
    **PS:更多精彩内容,请查看 --> 《AOSP 专栏》
    **PS:更多精彩内容,请查看 --> 《AOSP 专栏》

    展开全文
  • C/C++程序编译过程详解

    万次阅读 多人点赞 2017-11-13 14:51:06
    C/C++程序编译过程详解 C语言的编译链接过程要把我们编写的一个c程序(源代码)转换成可以在硬件上运行的程序(可执行代码),需要进行编译和链接。编译就是把文本形式源代码翻译为机器语言形式的目标文件...

    C/C++程序编译过程详解

    C语言的编译链接过程要把我们编写的一个c程序(源代码)转换成可以在硬件上运行的程序(可执行代码),需要进行编译和链接。编译就是把文本形式源代码翻译为机器语言形式的目标文件的过程。链接是把目标文件、操作系统的启动代码和用到的库文件进行组织,形成最终生成可执行代码的过程。过程图解如下:

    clip_image002

    从图上可以看到,整个代码的编译过程分为编译和链接两个过程,编译对应图中的大括号括起的部分,其余则为链接过程。

    1. 编译过程

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

    编译

    编译是读取源程序(字符流),对之进行词法和语法的分析,将高级语言指令转换为功能等效的汇编代码,源文件的编译过程包含两个主要阶段:

    编译预处理

    读取c源程序,对其中的伪指令(以# 开头的指令)和特殊符号进行处理。

    伪指令主要包括以下四个方面:

    1) 宏定义指令,如# define Name TokenString,# undef等。

    对于前一个伪指令,预编译所要做的是将程序中的所有Name用TokenString替换,但作为字符串常量的 Name则不被替换。对于后者,则将取消对某个宏的定义,使以后该串的出现不再被替换。

    2) 条件编译指令,如# ifdef,# ifndef,# else,# elif,# endif等。

    这些伪指令的引入使得程序员可以通过定义不同的宏来决定编译程序对哪些代码进行处理。预编译程序将根据有关的文件,将那些不必要的代码过滤掉。

    3) 头文件包含指令,如# include "FileName" 或者# include < FileName> 等。

    在头文件中一般用伪指令# define定义了大量的宏(最常见的是字符常量),同时包含有各种外部符号的声明。

    采用头文件的目的主要是为了使某些定义可以供多个不同的C源程序使用。因为在需要用到这些定义的C源程序中,只需加上一条# include语句即可,而不必再在此文件中将这些定义重复一遍。预编译程序将把头文件中的定义统统都加入到它所产生的输出文件中,以供编译程序对之进行处理。

    包含到c源程序中的头文件可以是系统提供的,这些头文件一般被放在/ usr/ include目录下。在程序中# include它们要使用尖括号(< >)。另外开发人员也可以定义自己的头文件,这些文件一般与c源程序放在同一目录下,此时在# include中要用双引号("")。

    4) 特殊符号,预编译程序可以识别一些特殊的符号。

    例如在源程序中出现的LINE标识将被解释为当前行号(十进制数),FILE则被解释为当前被编译的C源程序的名称。预编译程序对于在源程序中出现的这些串将用合适的值进行替换。

    预编译程序所完成的基本上是对源程序的“替代”工作。经过此种替代,生成一个没有宏定义、没有条件编译指令、没有特殊符号的输出文件。这个文件的含义同没有经过预处理的源文件是相同的,但内容有所不同。下一步,此输出文件将作为编译程序的输入而被翻译成为机器指令。

    编译、优化阶段

    经过预编译得到的输出文件中,只有常量;如数字、字符串、变量的定义,以及C语言的关键字,如main, if , else , for , while , { , } , + , - , * , \ 等等。

    编译程序所要作得工作就是通过词法分析和语法分析,在确认所有的指令都符合语法规则之后,将其翻译成等价的中间代码表示或汇编代码。

    优化处理是编译系统中一项比较艰深的技术。它涉及到的问题不仅同编译技术本身有关,而且同机器的硬件环境也有很大的关系。优化一部分是对中间代码的优化。这种优化不依赖于具体的计算机。另一种优化则主要针对目标代码的生成而进行的。

    对于前一种优化,主要的工作是删除公共表达式、循环优化(代码外提、强度削弱、变换循环控制条件、已知量的合并等)、复写传播,以及无用赋值的删除,等等。

    后一种类型的优化同机器的硬件结构密切相关,最主要的是考虑是如何充分利用机器的各个硬件寄存器存放有关变量的值,以减少对于内存的访问次数。另外,如何根据机器硬件执行指令的特点(如流水线、RISC、CISC、VLIW等)而对指令进行一些调整使目标代码比较短,执行的效率比较高,也是一个重要的研究课题。

    经过优化得到的汇编代码必须经过汇编程序的汇编转换成相应的机器指令,方可能被机器执行。

    汇编

    汇编过程实际上指把汇编语言代码翻译成目标机器指令的过程。对于被翻译系统处理的每一个C语言源程序,都将最终经过这一处理而得到相应的目标文件。目标文件中所存放的也就是与源程序等效的目标的机器语言代码。

    目标文件由段组成。通常一个目标文件中至少有两个段:

    1) 代码段:该段中所包含的主要是程序的指令。该段一般是可读和可执行的,但一般却不可写。

    2) 数据段:主要存放程序中要用到的各种全局变量或静态的数据。一般数据段都是可读,可写,可执行的。

    UNIX环境下主要有三种类型的目标文件:

    1) 可重定位文件

    其中包含有适合于其它目标文件链接来创建一个可执行的或者共享的目标文件的代码和数据。

    2) 共享的目标文件

    这种文件存放了适合于在两种上下文里链接的代码和数据。

    第一种是链接程序可把它与其它可重定位文件及共享的目标文件一起处理来创建另一个目标文件;

    第二种是动态链接程序将它与另一个可执行文件及其它的共享目标文件结合到一起,创建一个进程映象。

    3) 可执行文件

    它包含了一个可以被操作系统创建一个进程来执行之的文件。

    汇编程序生成的实际上是第一种类型的目标文件。对于后两种还需要其他的一些处理方能得到,这个就是链接程序的工作了。

    2. 链接过程

    由汇编程序生成的目标文件并不能立即就被执行,其中可能还有许多没有解决的问题。

    例如,某个源文件中的函数可能引用了另一个源文件中定义的某个符号(如变量或者函数调用等);在程序中可能调用了某个库文件中的函数,等等。所有的这些问题,都需要经链接程序的处理方能得以解决。

    链接程序的主要工作就是将有关的目标文件彼此相连接,也即将在一个文件中引用的符号同该符号在另外一个文件中的定义连接起来,使得所有的这些目标文件成为一个能够被操作系统装入执行的统一整体。

    根据开发人员指定的同库函数的链接方式的不同,链接处理可分为两种:

    1) 静态链接

    在这种链接方式下,函数的代码将从其所在的静态链接库中被拷贝到最终的可执行程序中。这样该程序在被执行时这些代码将被装入到该进程的虚拟地址空间中。静态链接库实际上是一个目标文件的集合,其中的每个文件含有库中的一个或者一组相关函数的代码。

    2) 动态链接

    在此种方式下,函数的代码被放到称作是动态链接库或共享对象的某个目标文件中。链接程序此时所作的只是在最终的可执行程序中记录下共享对象的名字以及其它少量的登记信息。在此可执行文件被执行时,动态链接库的全部内容将被映射到运行时相应进程的虚地址空间。动态链接程序将根据可执行程序中记录的信息找到相应的函数代码。

    对于可执行文件中的函数调用,可分别采用动态链接或静态链接的方法。使用动态链接能够使最终的可执行文件比较短小,并且当共享对象被多个进程使用时能节约一些内存,因为在内存中只需要保存一份此共享对象的代码。但并不是使用动态链接就一定比使用静态链接要优越。在某些情况下动态链接可能带来一些性能上损害。

    3. GCC的编译链接

    我们在linux使用的gcc编译器便是把以上的几个过程进行捆绑,使用户只使用一次命令就把编译工作完成,这的确方便了编译工作,但对于初学者了解编译过程就很不利了,下图便是gcc代理的编译过程:

    clip_image004

    从上图可以看到:

    1) 预编译

    将.c 文件转化成 .i文件

    使用的gcc命令是:gcc –E

    对应于预处理命令cpp

    2) 编译

    将.c/.h文件转换成.s文件

    使用的gcc命令是:gcc –S

    对应于编译命令 cc –S

    3) 汇编

    将.s 文件转化成 .o文件

    使用的gcc 命令是:gcc –c

    对应于汇编命令是 as

    4) 链接

    将.o文件转化成可执行程序

    使用的gcc 命令是: gcc

    对应于链接命令是 ld

    总结起来编译过程就上面的四个过程:预编译处理(.c) --> 编译、优化程序(.s、.asm)--> 汇编程序(.obj、.o、.a、.ko) --> 链接程序(.exe、.elf、.axf等)。

    4. 总结

    C语言编译的整个过程是非常复杂的,里面涉及到的编译器知识、硬件知识、工具链知识都是非常多的,深入了解整个编译过程对工程师理解应用程序的编写是有很大帮助的,希望大家可以多了解一些,在遇到问题时多思考、多实践。

    一般情况下,我们只需要知道分成编译和链接两个阶段,编译阶段将源程序(*.c) 转换成为目标代码(一般是obj文件,至于具体过程就是上面说的那些阶段),链接阶段是把源程序转换成的目标代码(obj文件)与你程序里面调用的库函数对应的代码连接起来形成对应的可执行文件(exe文件)就可以了,其他的都需要在实践中多多体会才能有更深的理解。

     


    C/C++编译过程

    C/C++编译过程主要分为4个过程 
    1) 编译预处理 
    2) 编译、优化阶段 
    3) 汇编过程 
    4) 链接程序

    一、编译预处理

    (1)宏定义指令,如#define Name TokenString,#undef等。 对于前一个伪指令,预编译所要做的是将程序中的所有Name用TokenString替换,

    但作为字符串常量的 Name则不被替换。对于后者,则将取消对某个宏的定义,使以后该串的出现不再被替换。

    (2)条件编译指令,如#ifdef,#ifndef,#else,#elif,#endif等。 这些伪指令的引入使得程序员可以通过定义不同的宏来决定编译程序对哪些代码进行处理。

    预编译程序将根据有关的文件,将那些不必要的代码过滤掉

    (3) 头文件包含指令,如#include "FileName"或者#include <FileName>等。 在头文件中一般用伪指令#define定义了大量的宏(最常见的是字符常量),

    同时包含有各种外部符号的声明。 包含到c源程序中的头文件可以是系统提供的,这些头文件一般被放在/usr/include目录下。

    在程序中#include它们要使用尖括号(< >)。

    另外开发人员也可以定义自己的头文件,这些文件一般与c源程序放在同一目录下,此时在#include中要用双引号("")。

    (4)特殊符号,预编译程序可以识别一些特殊的符号。 例如在源程序中出现的#line标识将被解释为当前行号(十进制数), 
    上面程序实现了对宏line的运用

    (5)预处理模块 预处理工作由#pragma命令完成,#Pragma命令将设定编译器的状态或者是指示编译器完成一些特定的动作。

    #pragma指令对每个编译器给出了一个方法,在保持与C和C++语言完全兼容的情况下,给出主机或操作系统专有的特征。

    依据定义,编译指示是机器或操作系统专有的,且对于每个编译器都是不同的。 
    打开C标准库函数,如stdio.h,我们总能找到下面这一句指示编译器初始化堆栈

    复制代码
    #include "iostream"
    #line 100
    using namespace std;
    int main(int argc, char* argv[])
    {
    cout<<"__LINE__:"<<__LINE__<<endl;
    return 0;
    }
    复制代码

    /*-------------------- 
    * 输出结果为: 
    * __LINE__:103 
    * 本来输出的结果应该是 7,但是用#line指定行号之后,使下一行的行号变为, 
    * 到输出语句恰为行103 
    ---------------------*/ 
    C/C++编译过程 
    或者程序指示编译器去链接系统动态链接库或用户自定义链接库 
    二、编译、优化阶段 
    经过预编译得到的输出文件中,只有常量;如数字、字符串、变量的定义,以及C语言的关键字,如main,if,else,for,while,{,}, +,-,*,\等等。 
    在《编译原理》中我们可以了解到一个编译器对程序代码的编译主要分为下面几个过程: 
    a) 词法分析 
    b) 语法分析 
    c) 语义分析 
    d) 中间代码生成 
    e) 代码优化 
    f) 代码生成 
    g) 符号表管理 
    h) 将多个步骤组合成趟 
    i) 编译器构造工具
     
    在这里我们主要强调对函数压栈方式(函数调用约定)的编译处理 
    C与C++语言调用方式大体相同,下面是几种常用的调用方式:

    __cdecl 是C DECLaration的缩写(declaration,声明),表示C语言默认的函数调用方法:所有参数从右到左依次入栈,

    这些参数由调用者清除,称为手动清栈。被调用函数不需要求调用者传递多少参数,调用者传递过多或者过少的参数,

    甚至完全不同的参数都不会产生编译阶段的错误。

    _stdcall 是StandardCall的缩写,是C++的标准调用方式:所有参数从右到左依次入栈,如果是调用类成员的话,

    最后一个入栈的是this指针。这些堆栈中的参数由被调用的函数在返回后清除,使用的指令是 retnX,X表示参数占用的字节数,

    CPU在ret之后自动弹出X个字节的堆栈空间。称为自动清栈。函数在编译的时候就必须确定参数个数,

    并且调用者必须严格的控制参数的生成,不能多,不能少,否则返回后会出错。

    PASCAL 是Pascal语言的函数调用方式,在早期的c/c++语言中使用这种调用方式,

    参数压栈顺序与前两者相反,但现在我们在程序中见到的都是它的演化版本,其实 

    复制代码
    #pragma comment(lib,_T("GDI32.lib"))
    #ifdef _MSC_VER
    /*
    * Currently, all MS C compilers for Win32 platforms default to 8 byte
    * alignment.
    */
    #pragma pack(push,_CRT_PACKING)
    #endif /* _MSC_VER */
    复制代码

    C/C++编译过程 
    质是另一种调用方式 
    _fastcall是编译器指定的快速调用方式。由于大多数的函数参数个数很少,使用堆栈传递比较费时。因此_fastcall通常规定将前两个(或若干个)参数由寄存器传递,其余参数还是通过堆栈传递。不同编译器编译的程序规定的寄存器不同。返回方式和_stdcall相当。 
    _thiscall 是为了解决类成员调用中this指针传递而规定的。_thiscall要求把this指针放在特定寄存器中,该寄存器由编译器决定。VC使用ecx,Borland的C++编译器使用eax。返回方式和_stdcall相当。 
    _fastcall 和 _thiscall涉及的寄存器由编译器决定,因此不能用作跨编译器的接口。所以Windows上的COM对象接口都定义为_stdcall调用方式。 
    C中不加说明默认函数为_cdecl方式(C中也只能用这种方式),C++也一样,但是默认的调用方式可以在IDE环境中设置。简单的我们可以从printf函数看出 
    printf使用从从左至右压栈,返回int型并由_CRTIMP指定封在动态链接库中。 
    通过金典的hello world程序我们可以知道编译器对其argc和argv[]这两个参数进行了压栈,并且argc留在了栈顶 
    优化处理是编译系统中一项比较艰深的技术。它涉及到的问题不仅同编译技术本身有关,而且同机器的硬件环境也有很大的关系。优化处理主要分为下面几个过程: 
    1) 局部优化 
    a) 基本块的划分 
    b) 基本块的变换 
    c) 基本块的DAG表示 
    d) DAG的应用 
    e) 构造算法讨论 
    2) 控制流分析和循环优化 
    a) 程序流图与循环 
    复制代码
    /*金典的hello world*/
    #include <stdio.h>
    int main(int argc, char* argv[])
    {
    printf("hello world");
    return 0;
    }
    _Check_return_opt_ _CRTIMP int __cdecl printf(_In_z_ _Printf_format_string_ const char * _Format, ...);
    #define CALLBACK _stdcall /* Windows程序回调函数*/
    #define WINAPI _stdcall
    #define WINAPIV _cdecl
    #define PASCAL _stdcall /*在c++语言中使用了StandardCall调用方式*/
    #define PASCAL _cdecl/*在c语言中使用了C DECLaration调用方式*/
    复制代码

    C/C++编译过程 
    b) 循环 
    c) 循环的查找 
    d) 可归约流图 
    e) 循环优化 
    3) 数据流的分析与全局优化 
    a) 一些主要的概念 
    b) 数据流方程的一般形式 
    c) 到达一定值数据流方程 
    d) 可用表达式及其数据流方程 
    e) 活跃变量数据流方程 
    f) 复写传播
     
    经过优化得到的汇编代码必须经过汇编程序的汇编转换成相应的机器指令,方可能被机器执行。

    三、汇编过程

    汇编过程实际上指把汇编语言代码翻译成目标机器指令的过程。对于被翻译系统处理的每一个C语言源程序,

    都将最终经过这一处理而得到相应的目标文件。目标文件中所存放的也就是与源程序等效的目标的机器语言代码。

    目标文件由段组成。通常一个目标文件中至少有两个段: 代码段:该段中所包含的主要是程序的指令。

    该段一般是可读和可执行的,但一般却不可写。 数据段:主要存放程序中要用到的各种全局变量或静态的数据。一般数据段都是可读,可写,可执行的。

    四、链接程序

    由汇编程序生成的目标文件并不能立即就被执行,其中可能还有许多没有解决的问题。

    例如,某个源文件中的函数可能引用了另一个源文件中定义的某个符号(如变量或者函数调用等);

    在程序中可能调用了某个库文件中的函数,等等。所有的这些问题,都需要经链接程序的处理方能得以解决。

    链接程序的主要工作就是将有关的目标文件彼此相连接,也即将在一个文件中引用的符号同该符号在另外一个文件中的定义连接起来,

    使得所有的这些目标文件成为一个能够诶操作系统装入执行的统一整体。

    根据开发人员指定的同库函数的链接方式的不同,链接处理可分为两种:

    (1)静态链接 在这种链接方式下,函数的代码将从其所在地静态链接库中被拷贝到最终的可执行程序中。

    这样该程序在被执行时这些代码将被装入到该进程的虚拟地址空间中。静态链接库实际上是一个目标文件的集合,

    其中的每个文件含有库中的一个或者一组相关函数的代码。

    (2) 动态链接 
    在此种方式下,函数的代码被放到称作是动态链接库或共享对象的某个目标文件中。链接程序此时所作的只是在最终的可执行程序中记录下共享对象的名字以及其它少量的登记信息。在此可执行文件被执行时,动态链接库的全部内容将被映射到运行时相应进程的虚地址空间。动态链接程序将根据可执行程序中记录的信息找到相应的函数代码。C/C++编译过程对于可执行文件中的函数调用,可分别采用动态链接或静态链接的方法。使用动态链接能够使最终的可执行文件比较短小,并且当共享对象被多个进程使用时能节约一些内存,因为在内存中只需要保存一份此共享对象的代码。但并不是使用动态链接就一定比使用静态链接要优越。在某些情况下动态链接可能带来一些性能上损害。

    ----------------------------------------------------作者  张彦升

    转自:https://www.cnblogs.com/mickole/articles/3659112.html

    展开全文
  • 编译原理_编译过程概述

    千次阅读 2019-04-11 01:26:12
    文章目录概述词法分析语法分析语义分析中间代码生成代码优化目标代码生成符号表管理(表格管理)出错处理 概述 编译程序即是将高级语言书写的源程序翻译成与之等价的目标...以中间代码为分水岭的原因是把编译过程分解...

    概述

    编译程序即是将高级语言书写的源程序翻译成与之等价的目标程序(汇编语言或机器语言)。其工作可分为六个阶段,见下图:

    Compile Procedure

    对于编译的各个阶段,逻辑上可以划分为前端和后端两部分。前端包括词法分析到中间代码生成中各个阶段的工作,后端则是优化及目标代码生成的阶段。

    以中间代码为分水岭的原因是把编译过程分解为与机器有关和无关两个部分。这样对同一种程序语言只需要一个前端,只要针对不同的机器开发不同的后端即可,前后端结合形成编译器。当语言改动时也只涉及前端部分的维护。而对于不同的程序语言只要有不同的前端,然后与同一个后端结合就可以得到程序语言在某种机器上的编译器了。

    下面简单介绍一下每个步骤(模块)的目的和工作,注意,下文步骤中使用的X,Y,Z等符号,在词法分析后实际上会是一个内部标识符,之所以照用原文只是为了方便理解。

    词法分析

    源程序可以看做是多行的字符串,词法分析是编译过程的第一阶段,其任务是对源程序逐字符扫描,从中识别出一个个“单词”,“单词”又叫做符号,它是程序语言的基本语法单位,如关键字(保留字)、标识符、常数、运算符、分隔符等。

    词法分析程序输出的“单词”以二元组的形式输出,即其种类和值。词法分析过程依据语言的词法规则,即描述“单词”结构的规则。

    如:

    PASCAL源程序一则:

    VAR X,Y,Z:real;
    X:= Y+Z*60;
    

    词法分析阶段将语句分割成以下单词序列:

    类型类型
    1. 关键字VAR9. 分号;
    2. 标识符X10. 标识符X
    3. 逗号,11. 赋值号:=
    4. 标识符Y12. 标识符Y
    5. 逗号,13. 加号+
    6. 标识符Z14. 标识符Z
    7. 冒号:15. 乘号*
    8. 标准标识符real16. 整常数60
    17. 分号;

    语法分析

    在词法分析结果的基础上,语法分析是根据语言的规则将单词符号序列分解成语法单位,如“表达式”、“语句”、”程序“等。语法规则就是语法单位的构成规则,通过语法分析确定整个输入串能否构成一个语法上正确的程序。如果程序没有错误,语法分析后就能正确的构造出 语法树;否则就会指出语法错误,并给出诊断。

    词法分析和语法分析本质上都是在对源程序的结构进行分析。

    如:

    根据上面词法分析的结果可以构造出的语法树如下图:

    Syntax trees

    语义分析

    语义分析阶段主要分析语法结构的含义,检查源程序是否包含静态语义错误,并收集类型信息供代码生成阶段使用。只有语法和语义都正确的源程序才能翻译成正确的目标程序。

    语义分析的主要工作就是对类型进行分析和检查,一般类型检查包括两点:类型载体及在其上的运算。如,整除取余运算符只能对整数使用,如果运算对象是浮点数就认为是类型错误。

    在确定源程序语法和语义后,就可以对其进行翻译并给出源程序的内部表示。对于声明语句,要记录所遇到的符号信息,此阶段还负责填写校验 符号表,对于可执行语句则分析其结构合理性,并补充必要的步骤。

    如:

    对于上面的变量声明语句VAR X,Y,Z:real;应生成下面的符号表(real类型占4位)

    符号类型逻辑地址
    Xreal0
    Yreal4
    Zreal8

    对于上面变量赋值语句X:= Y+Z*60;生成的语法树经过语义分析后应如下图,其中增加了一个语义处理点 inttoreal,它的作用是将整型数转换为浮点数:

    Syntax tree after analyse

    中间代码生成

    该阶段是根据语义分析的结果生成中间代码,“中间”即意味着并非可执行的机器码,它是一种简单含义明确的记号系统,有若干种形式,但所有中间代码的共同特征均为与具体机器无关,类似于算法伪代码的作用。最常用的中间代码是一种与汇编高度类似的三地址码,采用四元式实现,其形式为:(运算符, 运算对象1, 运算对象2, 运算结果)

    如:

    对于上述提到的赋值语句X:= Y+Z*60;可根据语义分析的结果生成以下四元序列式:

    1. (inttoreal, 60, -, t1)
    2. (*, Z, t1, t2)
    3. (+, Y, t2, t3)
    4. (:=, t3, -, X)

    其中t1, t2, t3均为编译程序生成的临时变量,用于存放临时的结果。

    代码优化

    由于编译器将源程序翻译成中间代码的工作是按固定模式进行的,因此可以发现中间代码中往往在时间和空间上均有较大浪费。当要生成高效的目标代码则必须进行优化,优化可以在中间代码生成阶段进行,也可以在目标代码生成阶段执行。

    由于中间代码与具体机器无关,因此对中间代码的优化主要是对程序的控制流和数据流的分析之上

    如:

    对上述赋值语句X:= Y+Z*60;生成的四元序列式优化,可以发现60是已知的常数,将它转换为浮点数60.0也可以在编译时完成,没有必要生成一个四元式。同时,发现t3的作用只是将结果传递给X,同样也不需要生成一个四元式。因此可优化成如下等价四元序列式:

    1. (*, Z, 60.0, t1)
    2. (+, Y, t1, X)

    当然这只是很简单的优化,实际上的优化要复杂的多,会涉及公共子表达式的提取等更多技术。

    目标代码生成

    目标代码生成是编译器的最后一个阶段,这一阶段将中间代码转化为目标机器上的绝对指令代码、可重定位指令代码或汇编指令代码,这一阶段与具体机器密切相关。

    如:

    使用两个寄存器R1和R2将上述的四元序列式生成下面的目标代码:

    1. MOVF Z, R2
    2. MULF #60.0, R2
    3. MOVF Y, R1
    4. ADDF R2, R1
    5. MOVF R1, X

    符号表管理(表格管理)

    符号表的主要作用就是记录符号的信息,以辅助语义检查和代码生成。在编译过程中需要对符号表进行快速的查找插入、修改、删除等操作。

    符号表的建立一般始于词法分析阶段,但也可以始于词法分析和语义分析阶段。有时候符号表的使用还会伴随到目标代码的运行阶段。

    出错处理

    源程序不可避免的会出现错误,这些错误大致分为静态错误和动态错误。

    动态错误又称为动态语义错误,它们发生在程序运行时,例如变量为0时做除数、引用数组元素下标错误等。

    静态错误是编译阶段发现的程序错误,可分为语法错误和静态语义错误,如关键字拼写错误、标点符号错误、表达式缺少操作数、括号不匹配等问题就是语法错误,语义错误主要指语义分析阶段发现的运算符与运算对象类型不合法等错误。

    在编译发生错误时,编译过程应想办法能够绕过去,以便在一次编译中发现尽可能多的错误。

    展开全文
  • C语言的编译过程

    千次阅读 2019-03-01 11:21:42
    编译编译程序读取源程序(字符流),对之进行词法和语法的分析,将高级语言指令转换为功能等效的汇编代码,再由汇编程序转换为机器语言,并且按照操作系统对可执行文件格式的要求链接生成可执行程序。  流程:C...
  • 编译过程概述: 编译程序完成从源程序到目标程序的翻译工作,是一个复杂的整体的过程。从概念上来讲,一个编译程序的整个工作过程是划分成阶段进行的,每个阶段将源程序的一种表示形式转换成另一种表示形式,各个...
  • Linux-C C语言编译过程

    千次阅读 2018-07-18 20:32:55
    Linux-C C语言编译过程 一、简述  GCC(GNU Compiler Collection,即 GNU 编译器套装),是一套由 GNU 开发的编程 语言编译器。简单介绍使用gcc编译器将hello.c文件编译成为hello可执行文件的过程。 在伪...
  • c语言的编译过程详解

    千次阅读 2020-10-05 14:19:08
    c语言的编译过程详解 IDE的使用让很多和我一样的人对C/C++可执行程序的底层生成一知半解,不利于我们深入理解原理。在这里小结一下,望路过的大神指正~ 前言:从一个源文件(.c文件)到可执行程序到底经历了哪几步,...
  • Android编译过程

    千次阅读 2018-08-21 18:36:13
    当前目录下编译执行,相当于在android目录下执行make 本文介绍Android的编译过程及各种mk文件的导入 1.编译 a.编译步骤 清理 sh $ make clobber 初始化环境 sh $ source ./build/envetup.sh # 这个...
  • linux程序编译过程

    千次阅读 2018-09-21 08:53:58
    高级语言需要通过翻译成机器语言才能执行,而翻译的方式分为两种,一种是编译型,另一种是解释型,因此我们基本上将高级语言分为两大类,一种是编译型语言,例如C,C++,Java,另一种是解释型语言,例如Python、Ruby...
  • 源程序到可执行程序的编译过程

    千次阅读 2019-09-19 22:49:47
    一份源代码,从开始产生到成为可执行程序的过程:预处理——编译——汇编——链接。 1、预处理 预处理又叫预编译,主要解释源文件中所有的预处理指令,包括头文件的展开和宏定义的替换,形成.i文件;具体细节...
  • GCC编译过程及基本命令总结

    千次阅读 多人点赞 2018-12-30 16:17:14
    GCC即GNU Compiler Collection,原本只是针对C语言的编译工具,现在已经变成了一个工具集,包含了C、C++、JAVA等语言的集合体。 管理和维护:由GNU项目负责。 二、GCC对C、C++的编译流程 (1) 预处理...
  • gcc程序的编译过程和链接原理

    万次阅读 多人点赞 2017-10-24 17:02:07
    一、C/C++文件的编译过程:先来看一下gcc的使用方法和常用选项 提示:gcc --help Ⅰ、使用方法:gcc [选项] 文件名Ⅱ、常用选项: 选项 含义 -v 查看gcc编译器的版本,显示gcc执行时的详细过程 -o ...
  • iOS编译过程的原理和应用

    万次阅读 多人点赞 2016-12-10 11:58:12
    前言 一般可以将编程语言分为两种,编译语言和直译式语言。...直译式语言不需要经过编译过程,而是在执行的时候通过一个中间的解释器将代码解释为CPU可以执行的代码。所以,较编译语言来说,直译式语言效率
  • 编译过程一般分为五个阶段

    千次阅读 2019-10-29 16:52:22
    下面是对编译过程的五个阶段的详解 对于编译程序的工作,从输入源程序开始到输出目标程序为止的整个过程,是非常复杂的。但就其过程而言,它与人们进行自然语言直接的翻译有许多相近之处。当我们把一种文字翻译为另...
  • 我们的代码会经过这4个环节,从而形成最终文件,c语言作为编译语言,用来向计算机发出指令。让程序员能够准确地定义计算机所需要使用的数据,并精确地定义在不同情况下所应当采取的行动。 预处理, 展开头文件/宏...
  • iOS 编译过程的原理和应用

    千次阅读 2017-04-14 09:10:20
    来源:黄文臣  blog.csdn.net/hello_hwc/article/details/53557308 ...编译语言在执行的时候,必须先通过编译器生成机器码,机器码可以直接在CPU上执行,所以执行效率较高。 像JavaScript,Python都是
  • clang 编译过程

    千次阅读 2019-03-24 22:05:18
    宏定义展开、头文件展开、条件编译等,同时将代码中的注释删除,这里并不会检查语法 2.编 译:gcc -S hello.i -o hello.s 检查语法,将预处理后文件编译生成汇编文件 3.汇 编:gcc -c hello.s -o hello.o 将汇编...
  • Linux-uboot-学习笔记(5):uboot的配置和编译过程代码分析 在Linux-基础入门-学习笔记(3):uboot常用命令与环境变量一文中,已经对uboot的基本认识有了一个简单的介绍,也知道了uboot是引到操作系统启动和部署...
  • FFmpeg 详尽编译过程

    万次阅读 2018-02-08 10:32:26
    内容摘要 准备linux系统 ubuntu14.04 安装 git 安装 vim(vimcdoc-1.5.0.tar.gz) 安装 jdk(jdk-8u151-linux-x64.tar.gz) 安装 ndk(android-ndk-r10e-linux-x86_64.bin) ...将已编译的FFmpeg 移植到 android stu...
  • python编译过程和执行原理

    万次阅读 多人点赞 2017-09-26 17:46:10
    python编译过程和执行原理 (1)python执行原理 这里的解释执行是相对于编译执行而言的。我们都知道,使用C/C++之类的编译性语言编写的程序,是需要从源文件转换成计算机使用的机器语言,经过链接器链接之后形成了...
  • make 显示详细编译过程

    千次阅读 2019-08-12 15:59:35
    make VERBOSE=1 或 make V=1
  • RISC-V嵌入式开发准备篇1:编译过程简介

    万次阅读 多人点赞 2018-11-01 08:37:22
    本文的目的是对编译过程进行简单的科普与回顾,为后续详细介绍“RISC-V GCC工具链”和“RISC-V汇编语言程序设计”打下基础。
  • [Xcode]Xcode中的编译过程以及编译器

    千次阅读 2018-02-26 21:09:45
    编译过程基本的编译过程分为四个步骤:预处理(Pre-process):把宏替换,删除注释,展开头文件,产生 .i 文件。编译(Compliling):把之前的 .i 文件转换成汇编语言,产生 .s文件。汇编(Asembly):把汇编语言...
  • Javac编译过程

    千次阅读 2016-03-04 17:50:02
    Javac编译过程大致分为4个过程,分别是: 词法分析 语法分析 语义分析 代码生成 词法分析   词法分析是将源代码的字符流转变为标记(Token)集合,单个字符是程序编写过程的最小元素,而标记则是编译过程的...
  • Qt程序编译过程

    千次阅读 2018-04-07 23:25:40
    qmake是一个协助简化跨平台进行专案开发的构建过程的工具程式,Qt附带的工具之一 。qmake能够自动生成Makefile、Microsoft Visual Studio 专案文件 和 xcode 专案文件。不管源代码是否是用Qt写的,都能使用qmake,...
  • C#编译过程

    千次阅读 2017-04-03 22:55:24
    .NET的编译和运行过程与之类似,首先编写好的源代码,然后编译为微软中间语言代码,运行的时候即时编译为本地机器语言,同时.NET代码运行时有一个CLR环境来管理程序。如下图为.NET代码编译运行过程: 下面详细...
  • 【C语言】源代码的编译过程

    千次阅读 2019-09-13 20:33:16
    探索一下从C源代码到二进制可执行文件生成中间各个过程具体做了哪些工作。
  • Java代码编译过程简述

    万次阅读 多人点赞 2017-02-05 11:31:14
    Javac编译器,能将一种语言规范转化成另外一种语言规范,通常编译器都是将便于人理解的语言规范转化成机器容易理解的语言规范,如C/C++或者汇编语言都是将源代码直接编译成目标机器码,这个目标机器代码是CPU直接...
  • 让cmake在编译过程中打印编印信息

    千次阅读 2020-07-09 22:41:50
    方法1: 执行命令cmake时追加:-DCMAKE_VERBOSE_MAKEFILE=ON 方法2: 在CMakeLists.txt中添加:set(CMAKE_VERBOSE_MAKEFILEON ON) 方法3: make时追加:VERBOSE=1 推荐方法1或2

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,399,041
精华内容 559,616
关键字:

编译过程