C 为什么参数的函数大小增加到单词大小?
我读取了i386和AMD64的SystemV ABI。他们告诉我们,参数必须四舍五入到单词大小的倍数。我不明白为什么 这是目前的情况。如果将4个字符参数传递给i386体系结构上的函数,则每个字符参数将占用16个字节和4个字节。为所有4个参数只分配4个字节不是更有效吗 对齐不是答案。因为在这两种情况下,16字节堆栈对齐可能需要4-12字节的填充。将4个字符放在单个寄存器或堆栈位置将需要创建并随后提取各个参数,这在指令方面非常昂贵。请注意,即使您谈论的是堆栈,内存访问也应该非常快,因为它很可能位于缓存中 如果你真的想节省那么多空间,你仍然可以自己使用一个4字节的参数来完成。将4个字符放在一个寄存器或堆栈位置将需要创建并随后提取各个参数,这在指令方面是昂贵的。请注意,即使您谈论的是堆栈,内存访问也应该非常快,因为它很可能位于缓存中C 为什么参数的函数大小增加到单词大小?,c,linux,assembly,x86,abi,C,Linux,Assembly,X86,Abi,我读取了i386和AMD64的SystemV ABI。他们告诉我们,参数必须四舍五入到单词大小的倍数。我不明白为什么 这是目前的情况。如果将4个字符参数传递给i386体系结构上的函数,则每个字符参数将占用16个字节和4个字节。为所有4个参数只分配4个字节不是更有效吗 对齐不是答案。因为在这两种情况下,16字节堆栈对齐可能需要4-12字节的填充。将4个字符放在单个寄存器或堆栈位置将需要创建并随后提取各个参数,这在指令方面非常昂贵。请注意,即使您谈论的是堆栈,内存访问也应该非常快,因为它很可能位于缓
如果你真的想节省那么多空间,你仍然可以自己使用一个4字节的参数来实现。我认为最初的C作者着眼于可移植性和可维护性,而不是压缩每个字节和周期。并不是说C对资源不小心,而是做了适当的权衡 将每个参数提升到堆栈颗粒大小是有意义的,现在仍然如此。如果您不顾一切地想挤进去,您可以随时更换:
int f(int a, int b, int c, int d) { ... }
与
现代的C编译器对用户不是那么友好;或者更确切地说,他们唯一的朋友是一个名为benchmark的luser…我认为最初的C作者着眼于可移植性和可维护性,而不是压缩每个字节和周期。并不是说C对资源不小心,而是做了适当的权衡 将每个参数提升到堆栈颗粒大小是有意义的,现在仍然如此。如果您不顾一切地想挤进去,您可以随时更换:
int f(int a, int b, int c, int d) { ... }
与
现代的C编译器对用户不是那么友好;或者更确切地说,他们唯一的朋友是一个名叫benchmark的luser
这不是更有效率吗
您必须始终说出要优化的内容:
执行速度快
小程序大小
更少的堆栈使用
更简单的编译器
...
如果您想优化以减少堆栈使用,那么将字节传递给函数确实会更有效
但是,通常您希望优化以获得快速执行速度或较小的程序大小
与将参数移动到堆栈中的现代编译器不同,我知道90年代编写的大多数编译器将参数推送到堆栈中。如果编译器使用推送操作,将字节放入堆栈将相当复杂——这将使程序变慢和变长
请注意,我从未见过对参数执行pop操作
这不是更有效率吗
您必须始终说出要优化的内容:
执行速度快
小程序大小
更少的堆栈使用
更简单的编译器
...
如果您想优化以减少堆栈使用,那么将字节传递给函数确实会更有效
但是,通常您希望优化以获得快速执行速度或较小的程序大小
与将参数移动到堆栈中的现代编译器不同,我知道90年代编写的大多数编译器将参数推送到堆栈中。如果编译器使用推送操作,将字节放入堆栈将相当复杂——这将使程序变慢和变长
请注意,我从未见过对参数执行pop操作。我不擅长组装,但我不明白为什么它会很昂贵。如果您传递了字数参数,您得到的参数如下:mov-8%ebp%ecx mov-12%ebp%edx等。。在我的示例中,它将类似于movb-8%ebp%ecx movb-9%ebp%edx等。。也许我不明白什么。@yevhen:大多数参数都是在寄存器中传递的。@请注意,调用约定实际上将4个字符打包到一个寄存器中,如果它们是结构类型参数的成员。@Yevhenglushko AFAIK movb需要一个字节大小的目的地,即这些指令不存在。@Yevhenglushko是的,这可以工作,但是请注意,它仍然是一个不对齐的访问,因此它很可能读取4个字节,并在引擎盖下进行一些按摩。我不知道确切的性能,请随意在最近的CPU中测量!我不擅长组装,但我不明白为什么它会很昂贵。如果您传递了字数参数,您得到的参数如下:mov-8%ebp%ecx mov-12%ebp%edx等。。在我的示例中,它将类似于movb-8%ebp%ecx movb-9%ebp%edx等。。也许我不明白什么。@yevhen:大多数人都不明白
uments在寄存器中传递。@请注意,调用约定实际上将4个字符打包到一个寄存器中,如果它们是结构类型参数的成员。@Yevhenglushko AFAIK movb需要一个字节大小的目的地,即这些指令不存在。@Yevhenglushko是的,这会起作用,但请注意它仍然是一个不对齐的访问,因此,它很可能读取4个字节,并在引擎盖下进行一些按摩。我不知道确切的性能,请随意在最近的CPU中测量!你不能打开一个字符。您可以弹出一个单词。堆栈args约定至少可能重复:您不能弹出字符。您可以弹出一个字。堆栈参数约定至少可能重复:要获取当前调用约定中的参数,您应该执行mov 0x8%ebp%ecx mov 0xc%ebp%ebx mov 0x10%ebp%edx mov 0x14%ebp%eax,但在我的示例中,它将是movsbl 0x8%ebp%ecx movsbl 0x9%ebp%ebx movsbl 0xa%ebp%edx movsbl 0xb%ebp%eax是否较慢?@Yevhenglushko读取参数:否。问题是写入参数。在20世纪80年代或90年代,当SystemV调用约定被开发出来时,大多数编译器使用push指令将参数放在堆栈上。并且不能推送8位值。要获取当前调用约定中的参数,应执行mov 0x8%ebp%ecx mov 0xc%ebp%ebx mov 0x10%ebp%edx mov 0x14%ebp%eax,但在我的示例中,它将是movsbl 0x8%ebp%ecx movsbl 0x9%ebp%ebx movsbl 0xa%ebp%edx movsbl 0xb%ebp%eax是否较慢?@Yevhenglushko读取参数:否。问题是写入参数。在20世纪80年代或90年代,当SystemV调用约定被开发出来时,大多数编译器使用push指令将参数放在堆栈上。并且不能推送8位值。