C 未使用参数中的返回值
在这篇文章中,我看到了一个技巧,返回值是第二个未传入的参数C 未使用参数中的返回值,c,gcc,x86,return-value,kernighan-and-ritchie,C,Gcc,X86,Return Value,Kernighan And Ritchie,在这篇文章中,我看到了一个技巧,返回值是第二个未传入的参数 int f(i, j) { j = i; } int main() { return f(3); } 从它看来,当代码复制j=i时,它将结果存储在eax中,这恰好是返回值 f: pushq %rbp movq %rsp, %rbp movl %edi, -4(%rbp) movl %esi, -8(%rbp)
int f(i, j)
{
j = i;
}
int main()
{
return f(3);
}
从它看来,当代码复制j=i
时,它将结果存储在eax
中,这恰好是返回值
f:
pushq %rbp
movq %rsp, %rbp
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
movl -4(%rbp), %eax
movl %eax, -8(%rbp)
nop
popq %rbp
ret
main:
pushq %rbp
movq %rsp, %rbp
movl $3, %edi
movl $0, %eax
call f
popq %rbp
ret
那么,这仅仅是因为运气好吗?gcc是否对此进行了记录?它只适用于
-O0
,但它适用于一组我尝试过的i
、-m32
,以及一组不同版本的GCC。GCC-O0
喜欢计算返回值寄存器中的表达式,如果需要寄存器的话。(GCC-O0
通常只希望retval寄存器中有值,但这超出了将其作为第一个临时值的范围。)
我已经做了一些测试,它看起来确实像GCC-O0
故意跨多个ISA执行此操作,有时甚至使用额外的mov
指令或等效指令。IIRC我使一个表达式更复杂,因此求值结果在另一个寄存器中结束,但它仍然将其复制回retval寄存器
可以(在x86上)编译到内存目标inc或add的x++
之类的东西不会将值保留在寄存器中,但赋值通常会保留。因此,值得注意的是,GCC将函数体视为
这不是任何文档、保证或标准化的内容。这是一个实现细节,不是为了让您利用这样的优势 以这种方式“返回”值意味着您正在“GCC-O0”中编程,而不是C。代码高尔夫规则的措辞规定程序必须至少在一个实现上工作。但我的理解是,它们应该工作的原因是正确的,而不是因为一些副作用的实现细节。它们在clang上破裂不是因为clang不支持某些语言功能,只是因为它们不是e文是用C写的 打破启用优化也不酷;在代码高尔夫中,某种程度的UB通常是可以接受的,比如整数环绕或指针投射类型双关,这可能是人们有理由希望得到良好定义的东西。但这纯粹是滥用一个编译器的实现细节,而不是语言特性 我在下面的评论中论证了这一点(该评论错误地宣称它超越了GCC)。这个答案有4张反对票(在国际海事组织中应该得到更多),但有16张赞成票。因此,一些社区成员不同意这是可怕和愚蠢的
有趣的事实:在ISO C++(而不是C)中,执行失败的非代码>虚空函数是未定义的行为,即使调用方不使用结果。即使在GNU C++中也是如此;在
GCC甚至可能不会发出警告-Wall
\uuuu attribute\uuuu((始终内联))
)
传递第二个arg只会给您分配一些内容。它是否是函数arg并不重要。
i=i;
即使使用-O0
也会进行优化,因此您确实需要一个单独的变量。也只需i;
进行优化即可
有趣的事实:递归的f(i){f(i);}
函数体在复制到第一个arg传递寄存器之前,确实会通过EAX反弹i
。所以GCC非常喜欢EAX
movl -4(%rbp), %eax
movl %eax, %edi
movl $0, %eax # without a full prototype, pass # of FP args in AL
call f
<>代码> i++;不加载到EAX中;它只使用内存目的地<代码>添加<代码>,不加载到登记器中。值得尝试使用GCC—O0为ARM。< /P>链接的页面中的代码有<代码> i= j;< />代码。这个代码中有“代码> j= i;< /Cuff>,这是交换的。- c+C++的例子,非空虚函数结束。GCC警告和代码生成中的行为差异。