相关文章:

C#的三大难点之前传:什么时候应该使用C#?
C#的三大难点之一:byte与char,string与StringBuilder
C#的三大难点之二:托管与非托管
C#的三大难点之三:消息与事件

托管代码与非托管代码

众所周知,我们正常编程所用的高级语言,是无法被计算机识别的。需要先将高级语言翻译为机器语言,才能被机器理解和运行。
在标准C/C++中,编译过程是这样的:

源代码首先经过预处理器,对头文件以及宏进行解析,然后经过编译器,生成汇编代码,接着,经过汇编,生成机器指令,最后将所有文件连接起来。
这种编译方式的优点在于,最终直接生成了机器码,可以直接被计算机识别和运行,无需任何中间运行环境,但缺点也在于,由于不同平台能够识别的机器码不同,因此程序的跨平台能力较差。
而在Java语言中,源代码并没有被直接翻译成机器码,而是编译成了一种中间代码(字节码Bytecode)。因此,运行Java程序需要一个额外的JRE(Java Runtime Enviromental)运行环境,在JRE中存在着JVM(Java Virtual Mechinal,Java虚拟机),在程序运行的时候,会将中间代码进一步解释为机器码,并在机器上运行。
使用中间代码的好处在于,程序的跨平台性比较好,一次编译,可以在不同的设备上运行。
托管/非托管是微软的.net framework中特有的概念,其中,非托管代码也叫本地(native)代码。与Java中的机制类似,也是先将源代码编译成中间代码(MSIL,Microsoft Intermediate Language),然后再由.net中的CLR将中间代码编译成机器代码。
而C#与Java的区别在于,Java是先编译后解释,C#是两次编译。
托管的方式除了拥有跨平台的优点之外,对程序的性能也产生一定的影响。但程序性能不在本文讨论的范围,这里不在赘述。
此外,在.net中,C++也可以进行托管扩展,从而使C++代码也依赖于.net和CLR运行,获得托管代码的优势。

托管资源与非托管资源

在上一节中,我们讲到,托管代码与非托管代码相比,有下列不同:

  1. 编译运行过程不同
  2. 跨平台能力不同
  3. 程序性能不同

本节中,我们会涉及到托管和非托管的另一个区别:

  1. 释放资源的方式不同

在C/C++中,资源都是需要手动释放的,比如,你new了一个指针,用过之后就需要delete掉,否则就会造成内存泄露。
而在Java中,不必考虑资源释放的问题,Java的垃圾回收机制(GC,Garbage Collection)会保证失效的资源被自动释放。
而C#的机制与Java类似,运行于.net平台上的代码,分配的资源一般会自动由平台的垃圾回收器释放,这样的资源就是托管资源。
但是一些例外的资源,如System.IO.StreamReader等各种流、各种连接所分配的资源,需要显式调用Close()或Dispose()释放,这种资源就叫做非托管资源。

托管与非托管的混合编程

C#的三大难点之前传:什么时候应该使用C#?中我提到过,C#的一大优势在于Windows平台下的界面编程。但由于C#并不是很普及,经常出现底层或后台代码采用C/C++编写的情况,此时,若选择C#作为界面语言,则必然遇到一个C#调用C++代码的问题。
比较普遍的解决方案就是,先将C/C++的代码生成为DLL动态运行库,再在C#中调用。
举个例子
在C中:

#include
#include 

void DisplayHelloFromDLL()
{
    printf ("Hello from DLL !\n");
}

void CallHelloFromDLL(char* cp)
{
    printf (cp);
    printf ("\n");
    *cp='a';
    cp++;
    printf (cp);
    printf ("\n");
}

在C#中:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TestConsole
{
    using System;
    using System.Runtime.InteropServices;     // DLL support

    class Program
    {
        [DllImport(@"TestLib.dll")]
        public static extern void DisplayHelloFromDLL();

        [DllImport(@"TestLib.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern void CallHelloFromDLL(StringBuilder s);

        static void Main()
        {
            Console.WriteLine("This is C# program");
            DisplayHelloFromDLL();
            StringBuilder sb = new StringBuilder(100);
            CallHelloFromDLL(sb);
            Console.WriteLine(sb);
    }
}

在混合编程中,涉及了几个要点。

  1. 如何在DLL中将函数接口暴露出来?
    有两种方式,一种是采用__declspec(dllexport)的声明,另一种是编写额外的def文件,如

    ;导出DLL函数
    LIBRARY testLib
    EXPORTS
    DisplayHelloFromDLL
    CallHelloFromDLL
    
  2. DLL与C#之间如何进行数据传送?
    这个问题其实很复杂,像int,double这种基本的数据类型,是很好传递的。到了byte和char,就有点复杂了,更复杂的还有string和stringBuilder,以及结构体的传递等。
    若传递的是指针,有两种方法,一种是采用托管的方式,使用Intptr存储指针,并使用ref获得地址(&);另一种是在C#中编写非托管的代码,用unsafe声明:

    unsafe
    {
    //非托管代码
    }
    

    在非托管代码中,即可进行指针相关的操作。
    若传递的是函数指针,由于C#中没有函数指针的概念,因此采用委托(delegate)的方式。 
    若传递的是自定义结构体,也可以采用ref的方式传递。
    这个如果有机会的话,我会单独整理一下。

  3. extern “C”、CallingConvention =CallingConvention.Cdecl)等必要声明。
    这里面也牵涉到复杂的语言机制,本文不再赘述。

参考文献:
[译]C++, Java和C#的编译过程解析
编译原理

预处理器是在真正的编译开始之前由编译器调用的独立程序。预处理器可以删除注释、包含其他文件以及执行宏(宏macro是一段重复文字的简短描写)替代。

汇编大多是指汇编语言汇编程序。把汇编语言翻译机器语言的过程称为汇编。在汇编语言中,用助记符(Memoni)代替操作码,用地址符号(Symbol)或标号(Label)代替地址码。这样用符号代替机器语言的二进制码,就把机器语言变成了汇编语言。于是汇编语言亦称为符号语言。用汇编语言编写的程序,机器不能直接识别,要由一种程序将汇编语言翻译成机器语言,这种起翻译作用的程序叫汇编程序,汇编程序是系统软件中语言处理的系统软件。

由于汇编更接近机器语言,能够直接对硬件进行操作,生成的程序与其他的语言相比具有更高的运行速度,占用更小的内存,因此在一些对于时效性要求很高的程序、许多大型程序的核心模块以及工业控制方面大量应用。

高级语言为什么不直接编译成机器码,而编译成汇编代码?

原创 2016年11月24日 13:55:56
  • 1353

1.一般的编译器,是先将高级语言转换成汇编语言(中间代码),然后在汇编的基础上优化生成OBJ目标代码,最后Link成可执行文件。

2.高级语言为什么不直接编译成机器码,而编译成汇编代码?

1)其中有一个好处是方便优化和调试,因为编译器也是工具,也是机器,毕竟是机器生成的程序,不可以非常完美的,而汇编是机器指令的助记 符,一个汇编指令就对应一条机器指令(特殊指令除外),调试起来肯定会比机器指令方便的方便,这样优化起来也方便。

2)高级语言只需要编译成汇编代码就可以了,汇编代码到机器码的转换是由硬件实现即可,有必要用软件实现这样分层可以有效地减弱编译器编写的复杂性,提高了效率.就像网络通 讯的实现需要分成很多层一样,主要目的就是为了从人脑可分析的粒度来减弱复杂性。

3)如果把高级语言的源代码直接编译成机器码的话,那要做高级语言到机器码之间的映射,如果这样做的话,每个写编译器的都必须熟练机 器码。这个不是在做重复劳动么?

C#的三大难点之二:托管与非托管的更多相关文章

  1. C#的托管与非托管大难点

    托管代码与非托管代码 众所周知,我们正常编程所用的高级语言,是无法被计算机识别的.需要先将高级语言翻译为机器语言,才能被机器理解和运行.在标准C/C++中,编译过程是这样的:源代码首先经过预处理器,对 ...

  2. [转]C# 之DLL调用(托管与非托管)

    每种编程语言调用DLL的方法都不尽相同,在此只对用C#调用DLL的方法进行介绍.首先,您需要了解什么是托管,什么是非托管.一般可以认为:非托管代码主要是基于win 32平台开发的DLL,activeX ...

  3. C# 托管和非托管混合编程

    在非托管模块中实现你比较重要的算法,然后通过 CLR 的平台互操作,来使托管代码调用它,这样程序仍然能够正常工作,但对非托管的本地代码进行反编译,就很困难.   最直接的实现托管与非托管编程的方法就是 ...

  4. [.net 面向对象程序设计进阶] (8) 托管与非托管

    本节导读:虽然在.NET编程过程中,绝大多数内存垃圾回收由CLR(公共语言运行时)自动回收,但也有很多需要我们编码回收.掌握托管与非托管的基本知识,可以有效避免某些情况下导致的程序异常. 1.什么是托 ...

  5. C# using 三种使用方式 C#中托管与非托管 C#托管资源和非托管资源区别

    1.using指令.using + 命名空间名字,这样可以在程序中直接用命令空间中的类型,而不必指定类型的详细命名空间,类似于Java的import,这个功能也是最常用的,几乎每个cs的程序都会用到. ...

  6. 利用C#Marshal类实现托管和非托管的相互转换

    Marshal 类 命名空间:System.Runtime.InteropServices 提供了一个方法集,这些方法用于分配非托管内存.复制非托管内存块.将托管类型转换为非托管类型,此外还提供了在与 ...

  7. 有关 Azure IaaS VM 磁盘以及托管和非托管高级磁盘的常见问题解答

    本文将对有关 Azure 托管磁盘和 Azure 高级存储的一些常见问题进行解答. 托管磁盘 什么是 Azure 托管磁盘? 托管磁盘是一种通过处理存储帐户管理来简化 Azure IaaS VM 的磁 ...

  8. NET的堆和栈04,对托管和非托管资源的垃圾回收以及内存分配

    在" .NET的堆和栈01,基本概念.值类型内存分配"中,了解了"堆"和"栈"的基本概念,以及值类型的内存分配.我们知道:当执行一个方法的时 ...

  9. Oracle Data Provider for .NET的使用(托管与非托管(一))

    目录 简单的概述 简单的使用 非托管系统要求 托管驱动系统要求 其它的注意事项 ODP.NET版本说明 安装ODP.NET 安装非托管驱动 非托管驱动绿色配置 简单的概述 ODP.NET的含义是 Or ...

随机推荐

  1. 关于CI框架加入sphinx官方API接口文件的时候,需要注意的问题

    从sphinx下载的官方文件sphinxapi.php中类名为class SphinxClient 加入到CI框架,放在system/libraries/下,由于需要遵从CI框架libraries类名 ...

  2. 顺序栈操作--数据结构(C++)版

    最近学习数据结构,一开始接触感觉好难,颓废了一段时间,后来又重新翻开学习,突然感觉到很大的兴趣.对这些代码的运用都有了 一些新的认识.下面简单的讲述下最新学到的顺序栈,不知道大家学习的时候会不会有感觉 ...

  3. 使用jquery实现省市二级列表

    这里讲用到 jquery 的  each  遍历方法  追加 节点或元素方法  append  appendTO   以及 remove 清除节点 <script> $(function( ...

  4. 关于yii2 的db log 日志 错误处理errorHandler

    log 通过配置Web.config来完成 1 数据库增加 ‘前缀_log’表 2 配置Web.config 'bootstrap' => ['log'], 'components' => ...

  5. 在libGDX中使用Spine骨骼动画

    首先,github是个宝库,实践流的读者可以直接看例子进行学习 1.这是Spine官方给出的例子 https://github.com/EsotericSoftware/spine-superspin ...

  6. Python的网络编程[1] -> FTP 协议[1] -> 使用 pyftplib 建立 FTP 服务器

    使用 pyftplib 建立 FTP 服务器 pyftplib 主要用于建立 FTP Server,与 ftplib 建立的 Client 进行通信. 快速导航 1. 模块信息 2. 建立 FTP 服 ...

  7. logstash filter plugin

    1. 基本语法%{NUMBER:duration} %{IP:client} 2. 支持的数据类型默认会把所有的匹配都当作字符串,比如0.043, 想要转成浮点数,可以%{NUMBER:num:flo ...

  8. Xamarin XAML语言教程构建进度条ProgressBar

    Xamarin XAML语言教程构建进度条ProgressBar Xamarin XAML语言教程构建进度条ProgressBar,ProgressBar被称为进度条,它类似于没有滑块的滑块控件.进度 ...

  9. 将一个txt里的A和B谈话内容获取出来并分别保存到A和B的txt文件中

    import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream;import java.io.Fi ...

  10. 【NOI2016】优秀的拆分

    题目描述 如果一个字符串可以被拆分为 $AABB$ 的形式,其中 $A$ 和 $B$ 是任意非空字符串,则我们称该字符串的这种拆分是优秀的. 例如,对于字符串 aabaabaa,如果令 $A = \m ...