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

ves*_*san 9 .net c# dll exe compilation

我很好奇 - 有时我会在代码中进行更改,重新编译,然后将我的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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这产生了一个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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

第二版:

.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
Run Code Online (Sandbox Code Playgroud)

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

Fiz*_*izz 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域

部分(目标文件)的大小或磁盘上初始化数据的大小(图像文件).对于可执行映像,它必须是可选标头中FileAlignment的倍数.如果这小于VirtualSize,则该部分的其余部分为零填充.因为此字段是四舍五入的,而VirtualSize字段不是,所以它也可能大于VirtualSize.当某个部分仅包含未初始化的数据时,该字段应为0.

我假设即使是最后一部分也是如此填充,以便发出部分的链接器代码然后加载它们的加载器代码不必担心最后一部分的大小的特殊情况.无论如何修剪最后一个部分将是一个相当毫无意义的优化,因为磁盘扇区(以及该文件系统的较大集群顶部)具有内部碎片,可以消除任何此类"保存"(从修改最后一部分)大部分时间.


Chr*_*ung 5

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