跟随this问题,我能够创建等效的汇编程序,其大小为39.6Kb。
这让我非常惊讶,因为我预计汇编程序应该比C程序小。正如问题所示,它使用了C头文件和gcc编译器。这会使汇编程序变大吗?还是它们通常大小相近?
使用strip
命令,我缩小了两个文件。这样可以去除调试代码,现在两个文件的大小非常相似,都是18.5Kb。
test.c:
unsigned int fun ( unsigned int a, unsigned int b)
{
return(a+b+1);
}
00000000 <fun>:
0: e52db004 push {r11} ; (str r11, [sp, #-4]!)
4: e28db000 add r11, sp, #0
8: e24dd00c sub sp, sp, #12
c: e50b0008 str r0, [r11, #-8]
10: e50b100c str r1, [r11, #-12]
14: e51b2008 ldr r2, [r11, #-8]
18: e51b300c ldr r3, [r11, #-12]
1c: e0823003 add r3, r2, r3
20: e2833001 add r3, r3, #1
24: e1a00003 mov r0, r3
28: e28bd000 add sp, r11, #0
2c: e49db004 pop {r11} ; (ldr r11, [sp], #4)
30: e12fff1e bx lr
并且这个
00000000 <fun>:
0: e2811001 add r1, r1, #1
4: e0810000 add r0, r1, r0
8: e12fff1e bx lr
由于不同的设置,指令数量为13条而不是3条,大小超过4倍。
一个人可能会直接从C语言中生成这个,没有花哨的东西。
add r0,r0,r1
add r0,r0,#1
bx lr
代码大小约为39 KB,与使用的编译器和语言无关(C/C++或ASM),不同的优化、调试信息等可能会使这段小程序的大小变化,但不会超过1000字节。我将用此程序进行测试构建。
#include <Windows.h>
#include <stdio.h>
void ep(void*)
{
ExitProcess(printf("Hello, World"));
}
/INCREMENTAL:NO /NOLOGO /MANIFEST:NO /NODEFAULTLIB
/SUBSYSTEM:CONSOLE /OPT:REF /OPT:ICF /LTCG /ENTRY:"ep" /MACHINE:X64 kernel32.lib msvcrt.lib
他得到了大小为2560字节的x86/x64 exe文件。
有什么不同吗?在于/NODEFAULTLIB
和我的版本中的msvcrt.lib
——它是纯导入库。
其余的35kb+大小是由于使用静态链接c运行时所引起的。即使你用汇编语言编写程序,你也需要使用一些库来链接printf
。而你的库包含了一些与你的代码静态链接的代码。这就是这35kb的原因。
任务不是c++对asm的比较——这里没有区别。任务是使用c-runtime或者不使用它。
我同意old_time的观点,但我也进行了一项快速的测试来获得基准数据。使用VS-2017 Pro编译后,如果我查看调试输出文件夹,可得到类似的结果(约37KB);但是在发布版本中,它更接近于9KB。其中很大一部分差异在于需要调用操作系统/C运行时DLL的静态库的大小。
编辑:尽管大多数现代C编译器可以匹配或超越大多数手写汇编代码,但手写汇编代码之所以可以更小,是因为它不必具备所有C运行时的开销,但这种差异很少足以证明汇编器代码的额外开发和维护成本,特别是对于非平凡应用程序而言。现代操作系统内核主要使用C或其他高级语言编写,并仅在少数关键函数中使用针孔汇编优化,这也是有原因的。
简单的“hello world”类程序并不适合比较C和汇编。因为编译器或人类在优化方面没有太多机会。编写一个数学或数据处理库和应用程序并进行比较。我敢打赌编译器会胜过你。
printf
和exit
),并将95%的代码实现交给了C运行时库(你可能相当低估了“底层”所做的工作量)。这对于你的汇编版本和C版本来说都是一样的,所以不奇怪你最终得到的可执行文件大小大致相同。最小的Windows PE可执行文件被认为是133字节,我没有检查DOS头区域是否还有足够的空间进行快速而简单的“Hello World”输出,可能没有,但我们可以说200B可能足够了。剩下的39kB是方便和C运行时库。 - Ped7g