問題9
exe文件的工作原理
解決方案
HelloWorld.cs
//HelloWorld.cs by Cornfield,2001
//csc HelloWorld.cs
using System;
class HelloWorld
{
public static void Main()
{
Console.WriteLine("Hello World !");
}
}
編譯輸出的HelloWorld.exe是一個由中間語言(IL),元數據(Metadata)和一個額外的被編譯器添加的目標平臺的標準可執行文件頭(比如Win32平臺就是加了一個標準Win32可執行文件頭)組成的PE(portable executable,可移植執行體)文件,而不是傳統的二進制可執行文件--雖然他們有着相同的擴展名。中間語言是一組獨立於CPU的指令集,它可以被即時編譯器Jitter翻譯成目標平臺的本地代碼。中間語言代碼使得所有Microsoft.NET平臺的高級語言C#,VB.NET,VC.NET等得以平臺獨立,以及語言之間實現互操作。元數據是一個內嵌於PE文件的表的集合。元數據描述了代碼中的數據類型等一些通用語言運行時(Common Language Runtime)需要在代碼執行時知道的信息。元數據使得.NET應用程序代碼具備自描述特性,提供了類型安全保障,這在以前需要額外的類型庫或接口定義語言(Interface Definition Language,簡稱IDL)。
這樣的解釋可能還是有點讓人困惑,那麼我們來實際的解剖一下這個PE文件。我們採用的工具是.NET SDK Beta2自帶的ildasm.exe,它可以幫助我們提取PE文件中的有關數據。我們鍵入命令"ildasm /output:HelloWorld.il HelloWorld.exe",一般可以得到兩個輸出文件:helloworld.il和helloworld.res。其中後者是提取的資源文件,我們暫且不管,我們來看helloworld.il文件。我們用"記事本"程序打開可以看到元數據和中間語言(IL)代碼,由於篇幅關係,我們只將其中的中間語言代碼提取出來列於下面,有關元數據的表項我們暫且不談:
// Microsoft (R) .NET Framework IL Disassembler. Version 1.1.4322.573
// Copyright (C) Microsoft Corporation 1998-2002. All rights reserved.
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z/V.4..
.ver 1:0:5000:0
}
.assembly Class2
{
// --- 下列自定義屬性會自動添加,不要取消註釋 -------
// .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(bool,
// bool) = ( 01 00 00 01 00 00 )
.hash algorithm 0x00008004
.ver 0:0:0:0
}
.module Class2.exe
// MVID: {A9D4A2DC-A401-4F5F-B16F-B3D40F584E59}
.imagebase 0x00400000
.subsystem 0x00000003
.file alignment 512
.corflags 0x00000001
// Image base: 0x070c0000
//
// ============== CLASS STRUCTURE DECLARATION ==================
//
.class public auto ansi beforefieldinit Test
extends [mscorlib]System.Object
{
} // end of class Test
// =============================================================
// =============== GLOBAL FIELDS AND METHODS ===================
// =============================================================
// =============== CLASS MEMBERS DECLARATION ===================
// note that class flags, 'extends' and 'implements' clauses
// are provided here for information only
.class public auto ansi beforefieldinit Test
extends [mscorlib]System.Object
{
.method private hidebysig static void Main() cil managed
{
.entrypoint
// 代碼大小 72 (0x48)
.maxstack 4
.locals init (int32[] V_0,
int32 V_1,
int32 V_2)
IL_0000: ldc.i4.5
IL_0001: newarr [mscorlib]System.Int32
IL_0006: stloc.0
IL_0007: ldc.i4.0
IL_0008: stloc.1
IL_0009: br.s IL_0015
IL_000b: ldloc.0
IL_000c: ldloc.1
IL_000d: ldloc.1
IL_000e: ldloc.1
IL_000f: mul
IL_0010: stelem.i4
IL_0011: ldloc.1
IL_0012: ldc.i4.1
IL_0013: add
IL_0014: stloc.1
IL_0015: ldloc.1
IL_0016: ldloc.0
IL_0017: ldlen
IL_0018: conv.i4
IL_0019: blt.s IL_000b
IL_001b: ldc.i4.0
IL_001c: stloc.2
IL_001d: br.s IL_003b
IL_001f: ldstr "arr[{0}]={1}"
IL_0024: ldloc.2
IL_0025: box [mscorlib]System.Int32
IL_002a: ldloc.0
IL_002b: ldloc.2
IL_002c: ldelem.i4
IL_002d: box [mscorlib]System.Int32
IL_0032: call void [mscorlib]System.Console::WriteLine(string,
object,
object)
IL_0037: ldloc.2
IL_0038: ldc.i4.1
IL_0039: add
IL_003a: stloc.2
IL_003b: ldloc.2
IL_003c: ldloc.0
IL_003d: ldlen
IL_003e: conv.i4
IL_003f: blt.s IL_001f
IL_0041: call int32 [mscorlib]System.Console::Read()
IL_0046: pop
IL_0047: ret
} // end of method Test::Main
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// 代碼大小 7 (0x7)
.maxstack 1
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method Test::.ctor
} // end of class Test
// =============================================================
//*********** 反彙編完成 ***********************
// WARNING: Created Win32 resource file Class2.res
我們粗略的感受是它很類似於早先的彙編語言,但它具有了對象定義和操作的功能。我們可以看到它定義並實現了一個繼承自System.Object 的HelloWorld類及兩個函數:Main()和.ctor()。其中.ctor()是HelloWorld類的構造函數,可在"HelloWorld.cs"源代碼中我們並沒有定義構造函數呀--是的,我們沒有定義構造函數,但C#的編譯器爲我們添加了它。你還可以看到C#編譯器也強制HelloWorld類繼承System.Object類,雖然這個我們也沒有指定。關於這些高級話題我們將在以後的講座中予以剖析。
那麼PE文件是怎麼執行的呢?下面是一個典型的C#/.NET應用程序的執行過程:
- 用戶執行編譯器輸出的應用程序(PE文件),操作系統載入PE文件,以及其他的DLL(.NET動態連接庫)。
- 操作系統裝載器根據前面PE文件中的可執行文件頭跳轉到程序的入口點。顯然,操作系統並不能執行中間語言,該入口點也被設計爲跳轉到mscoree.dll(.NET平臺的核心支持DLL)的_ CorExeMain()函數入口。
- CorExeMain()函數開始執行PE文件中的中間語言代碼。這裏的執行的意思是通用語言運行時按照調用的對象方法爲單位,用即時編譯器將中間語言編譯成本地機二進制代碼,執行並根據需要存於機器緩存。
- 程序的執行過程中,垃圾收集器負責內存的分配,釋放等管理功能。
- 程序執行完畢,操作系統卸載應用程序。
原文地址:http://www.microsoft.com/china/msdn/Archives/cornyfield/cornyfield.asp
問題10
Console.WriteLine()中{N}的含義
解決方案
using System;
public class Test
{
static void Main()
{
int[] arr = new int[5];
for(int i = 0;i < arr.Length;i++)
{
arr[i] = i * i;
}
for(int i = 0;i < arr.Length;i++)
{
Console.WriteLine("arr[{0}]={1}",i,arr[i]);
}
Console.Read();
}
}
在Console.WriteLine("arr[{0}]={1}",i,arr[i]);第一個參數是“格式化”,後面的參數是填充到格式中的參數。
如:{0}填充i,{1}填充arr[i]。最後的結果爲:"arr[i]="arr[i]。依次類推。