为什么代码中的小变化不会影响exe文件大小?

时间:2015-01-21 04:08:59

标签: c# .net dll compilation exe

我很好奇 - 有时候我会对代码进行更改,重新编译,然后将我的exe或dll文件复制到旧版本上,然后看到Windows告诉我文件的日期发生了变化,但大小保持不变。那是为什么?

作为示例,我使用以下控制台应用程序进行了测试:

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

namespace ConsoleApplication4
{
    class Program
    {
        static void Main(string[] args)
        {
            int a = 1;
            int b = 2;
            Console.WriteLine(a + b);
        }
    }
}

这产生了一个5120字节的exe文件(Visual Studio 2012,Debug build)。然后,我将代码更改为:

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

namespace ConsoleApplication4
{
    class Program
    {
        static void Main(string[] args)
        {
            int a = 1;
            int b = 2;
            int c = 3;
            Console.WriteLine(a + b + c);
        }
    }
}

exe的大小完全相同。

我看一下反汇编,它显示了IL代码的差异,因此不能将差异优化掉:

第一版:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       15 (0xf)
  .maxstack  2
  .locals init (int32 V_0,
           int32 V_1)
  IL_0000:  nop
  IL_0001:  ldc.i4.1
  IL_0002:  stloc.0
  IL_0003:  ldc.i4.2
  IL_0004:  stloc.1
  IL_0005:  ldloc.0
  IL_0006:  ldloc.1
  IL_0007:  add
  IL_0008:  call       void [mscorlib]System.Console::WriteLine(int32)
  IL_000d:  nop
  IL_000e:  ret
} // end of method Program::Main

第二版:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       19 (0x13)
  .maxstack  2
  .locals init ([0] int32 a,
           [1] int32 b,
           [2] int32 c)
  IL_0000:  nop
  IL_0001:  ldc.i4.1
  IL_0002:  stloc.0
  IL_0003:  ldc.i4.2
  IL_0004:  stloc.1
  IL_0005:  ldc.i4.3
  IL_0006:  stloc.2
  IL_0007:  ldloc.0
  IL_0008:  ldloc.1
  IL_0009:  add
  IL_000a:  ldloc.2
  IL_000b:  add
  IL_000c:  call       void [mscorlib]System.Console::WriteLine(int32)
  IL_0011:  nop
  IL_0012:  ret
} // end of method Program::Main

如果代码在物理上更大,那么文件的大小如何完全相同?这只是一些随机的机会吗?它发生在我身上很多(当对代码进行小的更改时)......

2 个答案:

答案 0 :(得分:6)

来自https://msdn.microsoft.com/en-us/library/ms809762.aspx

  

DWORD FileAlignment

     

在PE文件中,保证包含每个部分的原始数据以此值的倍数开始。默认值为0x200字节,可能是为了确保节始终始于磁盘扇区的开头(长度也为0x200字节)。此字段等效于NE文件中的段/资源对齐大小。与NE文件不同,PE文件通常没有数百个部分,因此通过对齐文件部分而浪费的空间几乎总是非常小。

编辑:磁盘上的所有部分大小也会向上舍入(填充)到FileAlignment的倍数。来自http://www.openwatcom.org/ftp/devel/docs/pecoff.pdf

  

SizeOfRawData域

     

部分(目标文件)的大小或大小   磁盘上的初始化数据(图像文件)。可执行文件   image,这必须是FileAlignment的倍数   可选的标题。如果这小于VirtualSize   该部分的其余部分为零填充。因为   此字段是圆形的,而VirtualSize字段则不是   它可能比VirtualSize大   好。当某个部分仅包含未初始化的数据时,   该字段应为0。

我认为即使是最后一部分也是如此填充,以便发出部分的链接器代码然后加载它们的加载器代码不必担心最后一部分的大小的特殊情况。无论如何修剪最后一个部分将是一个相当无意义的优化,因为磁盘扇区(并且在该文件系统的较大集群之上)具有internal fragmentation,这将消耗任何这样的“保存”(从修剪最后一部分)大多数时候。

答案 1 :(得分:5)

可执行文件包含许多部分。如果我没记错的话,这些部分中的每一部分都与512字节对齐。