汇编程序的大小是否与 C 程序几乎相同
Are Assembly programs almost the same size as C programs
例如:我创建了一个打印 "Hello, World" 的简单 C 程序,编译它并创建了一个大小为 39.8Kb 的可执行文件。
根据 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 条指令 vs 3 条指令,大 4 倍多。
人类可能会直接从 C 生成这个,没什么特别的
add r0,r0,r1
add r0,r0,#1
bx lr
从操作顺序上不确定您是否在技术上必须先将 1 添加到 b,然后再将该总和添加到 a。或者如果没关系。我从左到右,编译器从右到左。
所以你可以说编译器和我的程序集生成了相同数量的二进制字节,或者你可以说编译器生成了 4 倍多的二进制文件。
将上面的内容扩展成一个真正有用的程序。
练习 reader(OP,请不要破坏它)以弄清楚为什么编译器可以生成两个大小相差如此大的不同正确解决方案。
编辑
提到的 .exe、elf 和其他 "binary" 格式可以包含调试信息、包含 functions/labels 名称的 ascii 字符串,可以生成漂亮的调试屏幕。它们是 "binary" 的一部分,因为它们是行李的一部分,但不是机器代码,也不是执行该程序时使用的数据,至少不是我提到的东西。您可以在不更改程序所需的机器代码或数据的情况下,使用编译器设置来操纵 .exe 或其他文件格式的大小,因此相同的编译器-汇编程序-链接器或汇编程序-链接器路径可以使二进制文件在某种意义上通过包括或不包括此额外行李,该词变大或变小。因此,这是了解文件大小的一部分,以及为什么即使您的 hello world 程序大小不同,整个文件的大小可能大致相同,如果一个长 10 个字节但 .exe 为 40K,那么这 10 个字节在噪音。但是如果我理解你的问题,那 10 个字节就是你有兴趣了解它在编译 C 和手写 C 之间的比较。
另请注意,编译器是由人类制造的,因此它们产生的输出与至少那些人类可以产生的输出相当,其他人可以做得更好,许多人做得更差,具体取决于您对更好和更差的定义。
我同意 old_time 但我也对基本事实进行了快速测试。使用 VS-2017 Pro,我在可执行文件的大小上得到了类似的结果 (~37KB),但前提是我查看调试输出文件夹。在为发布构建之后,它接近 ~9KB。大部分差异在于调用 OS/C-runtime DLL 所需的静态库的大小。
编辑:尽管大多数现代 C 编译器可以匹配或胜过大多数手写的汇编代码,但手写的种类可能更小,因为它不必拥有所有 C 运行-time 开销,但差异很少足以保证汇编代码的额外开发和维护成本,特别是对于非平凡的应用程序。大多数现代 OS 内核主要用 C 或其他高级语言编写,只有少数关键函数采用针孔汇编程序优化,这是有原因的。
普通 "hello world" class 程序不是 C 与汇编程序的良好比较。编译器或人类没有足够的机会以优化的方式做很多事情。编写数学或数据处理库和应用程序并进行比较。我愿意打赌编译器会踢你的但是。
大小 39+ Kb 绝对与使用的编译器和语言无关(c/c++ 或 asm)不同优化、调试信息等——可以改变这个小代码的大小,比如说 1000 字节。但不会更多。我用于测试构建下一个程序
#include <Windows.h>
#include <stdio.h>
void ep(void*)
{
ExitProcess(printf("Hello, World"));
}
link其他选项:
/INCREMENTAL:NO /NOLOGO /MANIFEST:NO /NODEFAULTLIB
/SUBSYSTEM:CONSOLE /OPT:REF /OPT:ICF /LTCG /ENTRY:"ep" /MACHINE:X64 kernel32.lib msvcrt.lib
并为 x86/x64.
获得大小为 2560 字节的 exe
有什么不同?在 /NODEFAULTLIB
和我的 msvcrt.lib
版本中 - 这是纯导入库。
您使用静态 linked c 运行时提供的其余 35kb+ 大小。即使你在 asm 上编写程序——你需要为 link 到 printf
使用一些库。和你的库包含一些静态代码 linked with your code。在这个代码中这个 35kb.
task 不是 c++ vs asm - 这里没有什么不同。任务正在使用 c-runtime 或未使用
例如:我创建了一个打印 "Hello, World" 的简单 C 程序,编译它并创建了一个大小为 39.8Kb 的可执行文件。
根据 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 条指令 vs 3 条指令,大 4 倍多。
人类可能会直接从 C 生成这个,没什么特别的
add r0,r0,r1
add r0,r0,#1
bx lr
从操作顺序上不确定您是否在技术上必须先将 1 添加到 b,然后再将该总和添加到 a。或者如果没关系。我从左到右,编译器从右到左。
所以你可以说编译器和我的程序集生成了相同数量的二进制字节,或者你可以说编译器生成了 4 倍多的二进制文件。
将上面的内容扩展成一个真正有用的程序。
练习 reader(OP,请不要破坏它)以弄清楚为什么编译器可以生成两个大小相差如此大的不同正确解决方案。
编辑
提到的.exe、elf 和其他 "binary" 格式可以包含调试信息、包含 functions/labels 名称的 ascii 字符串,可以生成漂亮的调试屏幕。它们是 "binary" 的一部分,因为它们是行李的一部分,但不是机器代码,也不是执行该程序时使用的数据,至少不是我提到的东西。您可以在不更改程序所需的机器代码或数据的情况下,使用编译器设置来操纵 .exe 或其他文件格式的大小,因此相同的编译器-汇编程序-链接器或汇编程序-链接器路径可以使二进制文件在某种意义上通过包括或不包括此额外行李,该词变大或变小。因此,这是了解文件大小的一部分,以及为什么即使您的 hello world 程序大小不同,整个文件的大小可能大致相同,如果一个长 10 个字节但 .exe 为 40K,那么这 10 个字节在噪音。但是如果我理解你的问题,那 10 个字节就是你有兴趣了解它在编译 C 和手写 C 之间的比较。
另请注意,编译器是由人类制造的,因此它们产生的输出与至少那些人类可以产生的输出相当,其他人可以做得更好,许多人做得更差,具体取决于您对更好和更差的定义。
我同意 old_time 但我也对基本事实进行了快速测试。使用 VS-2017 Pro,我在可执行文件的大小上得到了类似的结果 (~37KB),但前提是我查看调试输出文件夹。在为发布构建之后,它接近 ~9KB。大部分差异在于调用 OS/C-runtime DLL 所需的静态库的大小。
编辑:尽管大多数现代 C 编译器可以匹配或胜过大多数手写的汇编代码,但手写的种类可能更小,因为它不必拥有所有 C 运行-time 开销,但差异很少足以保证汇编代码的额外开发和维护成本,特别是对于非平凡的应用程序。大多数现代 OS 内核主要用 C 或其他高级语言编写,只有少数关键函数采用针孔汇编程序优化,这是有原因的。
普通 "hello world" class 程序不是 C 与汇编程序的良好比较。编译器或人类没有足够的机会以优化的方式做很多事情。编写数学或数据处理库和应用程序并进行比较。我愿意打赌编译器会踢你的但是。
大小 39+ Kb 绝对与使用的编译器和语言无关(c/c++ 或 asm)不同优化、调试信息等——可以改变这个小代码的大小,比如说 1000 字节。但不会更多。我用于测试构建下一个程序
#include <Windows.h>
#include <stdio.h>
void ep(void*)
{
ExitProcess(printf("Hello, World"));
}
link其他选项:
/INCREMENTAL:NO /NOLOGO /MANIFEST:NO /NODEFAULTLIB
/SUBSYSTEM:CONSOLE /OPT:REF /OPT:ICF /LTCG /ENTRY:"ep" /MACHINE:X64 kernel32.lib msvcrt.lib
并为 x86/x64.
获得大小为 2560 字节的 exe有什么不同?在 /NODEFAULTLIB
和我的 msvcrt.lib
版本中 - 这是纯导入库。
您使用静态 linked c 运行时提供的其余 35kb+ 大小。即使你在 asm 上编写程序——你需要为 link 到 printf
使用一些库。和你的库包含一些静态代码 linked with your code。在这个代码中这个 35kb.
task 不是 c++ vs asm - 这里没有什么不同。任务正在使用 c-runtime 或未使用