如果所有的INT 3不够用,递增联接的文件还会有更多的浪费空间。在这种文件里, 每个文件里的函数还多了一个JMP指令。当你调用递增联接文件里的函数时, CALL指令找到相应的JMP,方执行所需要的函数。这些JMP的好处是使联接程序 可以在内存中任意移动函数而不用更新所有的使用这些函数的CALL指令。
总的说来,递增联接在开发时非常得心应手。只要你确认不要把递增联接的文件 发行出去。一般,当你在工程里把调试开关转到发行版本时递增联接功能会自动关闭。 如果你自己写MAKE文件的话,联接程序控制递增联接的方法是/INCREMENTAL:XX, XX可以是YES或NO。

去除调试信息
在程序里留下调试信息究竟会带给你多大的浪费空间,以及产生了什么样的调试信息, 依不同编译器而定。让我们先从非Microsoft的编译器开始,因为他们比较容易描述。 在非Microsoft的编译器中,调试信息一般都是可执行文件的一部份。 在一个长度可观的工程里,调试信息可以达到长度的百分之五十,甚至更多。
Microsoft的编译器与调试信息的故事更为复杂。一般,生成调试信息的同时也用 递增联接。如果使用了递增联接,Microsoft的联接器会把调试信息放在一个单独的以 PDB为后缀名的文件里 (PDB的意思是程序数据库program database) 。 这样做是为了递增联接时对可执行文件做最小的改动。PDB文件里是一些零碎的 CodeView风格的信息。你还记得CodeView吧?
用PDB存储信息的可执行文件用一小块地方来存储相应PDB文件的名字。 当使用递增联接和PDB文件时,可执行文件里因调试信息而浪费的空间是很小的, 仅仅是PDB文件的全路径的长度。技术上,这一段会象CodeView信息那样列出来, 但它确实只是CodeView信息的指针。
Microsoft调试信息常见的另一种类型是在可执行文件中实实在在的CodeView符号。 你可以用/PDB:NONE来强迫联接器生成CodeView信息,但这样做就关闭了递增联接的功能。 计算、读写CodeView符号与递增联接相冲突。如果你用CodeView调试信息, 那么EXE文件的浪费空间也象前面我所说的非Microsoft编译器一样能达到50%。
用Microsoft编译器你还可以生成COFF风格的符号。COFF符号是早期Windows NT 制作队伍做它的编程工具时的格式,并流行了下来。 联接器的开关可以是/DEBUGTYPE:COFF或/DEBUGTYPE:BOTH。 COFF符号只有相对少的工具,而且大多数都在Win32 SDK。 COFF调试信息象CodeView符号一样会占据可执行文件的很大空间, 所以在发行前你应该去除。
Microsoft编译器还能产生另一种调试信息,叫做FPO(Frame Pointer Omission)。 FPO用来连接CodeView或PDB符号。它在编译器没有用EBP寄存器生成标准堆栈桢 (a standard stack frame) 的地方帮助调试器查找函数的参数和本地变量。 FPO信息也很大,同样要在发行前去除。
最后,在Microsoft编译器生成的可执行文件里你可以看见混杂的调试信息。 这个区域通常是0x110字节,保存着联接器生成的可执行文件的名字。 如果你改了可执行文件的名字,调试器依然能用混杂调试信息来判定文件的原始名字, 然后算出对应的PDB文件的名字。在发行前非调试联接可执行文件就可以去除这混杂调试信息。
除了调试信息浪费的空间外,还有两个原因要求你关心你发行的文件里有没有调试信息。 第一,存在调试信息就表明编译时编译器的优化开关没打开。 优化开关可以很大程度地减小文件长度,下面我还会说到它。第二, 调试信息让你无法防范别人反求你的程序。记住,调试信息表明了你的代码、 数据、类型(例如类的定义)。有了调试信息,一个中等能力的程序员可以象 打开核桃一样crack你程序的内部机制。

使用优化开关
虽然编译器的优化开关被抨击产生BUG代码,我认为没有比放弃它更糟的事了。 依我经验,高度优化时产生的问题都是由于优化代码速度而不是代码长度引起的。 说实在话,在我让编译器为代码长度优化时我从来没有见过优化开关产生的BUG。
优化开关的好处不是它产生好的代码,一个优秀的汇编语言程序员能做到 甚至比优化开关做得更好。优化开关能把你从编译器可能产生的效率不高的代码中 ZHENJIU出来。下面我用Visual C++来做个例子,你能看见不同的编译器产生的 类似的结果。


 int foo( int i )

{

    return i * 2;

}



int main()

{

    if ( foo(7) )

        return 1;

    else

        return 0;

}






下一页