当前位置: 首页 > 知识库问答 >
问题:

函数返回值优化?

孙恩
2023-03-14

我正在编写自己的编程语言,由于各种原因,它编译为C。(其中之一是我对汇编知之甚少)。

我有一个关于编译器(例如GCC或Clang)如何优化从函数返回值的问题。假设我有这样的代码:

int FUNC()
{
    int A = 3;
    return A;
}

int main()
{
    int B = FUNC();
}

我的理解是,您可能希望变量A在从FUNC返回时复制到B(如果A和B是结构,这可能会很昂贵)。编译器会认识到在这种情况下,B可以指向a所在的位置,而不需要拷贝吗?

如果main()看起来像这样:怎么办?

int main()
{
    int C;
    C = FUNC();
}

谢谢大家!

共有3个答案

邓英卓
2023-03-14

好的编译器超越了您建议的优化。带有-O3的Apple Clang 11将您的main例程编译为:

_main:
    pushq   %rbp
    movq    %rsp, %rbp
    xorl    %eax, %eax
    popq    %rbp
    retq

因此,编译器已经超越了您合并AB的建议;它已经完全删除了它们。

如果我们包括<代码>

_main:
    pushq   %rbp
    movq    %rsp, %rbp
    leaq    L_.str(%rip), %rdi
    movl    $3, %esi
    xorl    %eax, %eax
    callq   _printf
    xorl    %eax, %eax
    popq    %rbp
    retq

现在AB没有被完全删除,但它们在指令中被简化为一个直接常数。

南门志
2023-03-14
匿名用户

一般来说,可以自由地写这样的东西:

int tmp = a + b;
int c = tmp;

< code>tmp将得到优化。

首先,您的函数将得到类似的优化,不分配< code>A,而只返回3,从只读存储器中取出(与机器码一起内联)。

函数内联的工作方式也类似,在这种情况下,整个函数调用将被替换为3。

现在如果我们添加一个类似打印的副作用,< code>printf("%d ",B);中,您的整个代码被优化为汇编程序,就像这个“伪代码汇编程序”一样:

  • 将3移动到寄存器x
  • 将字符串文字“%d”的地址移动到寄存器y
  • 调用printf(x, y)

在实际的x86 asm中,这将类似于:

mov     esi, 3
mov     edi, OFFSET FLAT:.LC0
call    printf

变量 A B 和函数都进行了优化。

萧晓博
2023-03-14

基本上有两种情况。(1)返回值是结构以外的东西,(2)返回值是结构。

对于第(1)种情况,返回值通常在寄存器中——它过去总是为r0,或者在浮点返回的情况下可能为f0,或者在< code>long int返回的情况下可能为r0 r1。

在这种情况下,当你有一些像

int a()
{
    return 3;
}

int b()
{
    return a();
}

编译器基本上将b()编译为调用函数a的代码,仅此而已。函数a在值函数返回的寄存器int-中返回其值,而这正是函数b返回它所需的位置,因此无需其他操作;该值已经位于它所属的位置。因此,至少在这种情况下,不需要额外的复制。

(这也可能导致看似“错误”的代码无论如何都可以工作的情况,因此这个常见问题:函数返回值而没有返回语句?。)

但是,在函数main中,您执行了int B=B()'s位置的“副本”。(尽管现在,智能编译器可能会记住“目前,r0B”。)

另一方面,对于结构(即情况 2),特别是对于大型结构,编译器通常会传递一个额外的隐藏参数,该参数是指向调用方中返回值应位于的位置的指针。也就是说,如果您有

struct largestruct bb();

int main()
{
    struct largestruct B;
    B = bb();
}

它或多或少会被编译,就像你写的一样

void bb(struct largestruct *);

int main()
{
    struct largestruct B;
    bb(&B);
}

所以如果你有

extern struct largestruct aa();

struct largestruct bb()
{
    return aa();
}

它可能会像编写一样编译

extern void aa(struct largestruct *);

void bb(struct largestruct *__retp)
{
    aa(__retp);
}

而且,同样,“指针指向正确的位置,并且不需要副本”或多或少是正确的。

 类似资料:
  • 在rust中,任何函数都有返回类型,当函数返回时,会返回一个该类型的值。我们先来看看main函数: fn main() { //statements } 之前有说过,函数的返回值类型是在参数列表后,加上箭头和类型来指定的。不过,一般我们看到的main函数的定义并没有这么做。这是因为main函数的返回值是(),在rust中,当一个函数返回()时,可以省略。main函数的完整形式如下:

  • Lua 具有一项与众不同的特性,允许函数返回多个值。Lua 的库函数中,有一些就是返回多个值。 示例代码:使用库函数 string.find,在源字符串中查找目标字符串,若查找成功,则返回目标字符串在源字符串中的起始位置和结束位置的下标。 local s, e = string.find("hello world", "llo") print(s, e) -->output 3 5 返回多个值

  • 我一直在做这个岩石剪刀剪刀程序一段时间,它没有显示的方式,它应该是应该的。我在代码中使用了值返回函数。问题在程序的末尾显现出来。游戏的结果将被替换,但实际显示的是另一个提示,供用户输入他们的选择。下面是我的代码:

  • 通过上面的学习,可以知道通过 return [表达式] 语句用于退出函数,选择性地向调用方返回一个表达式。 不带参数值的 return 语句返回 None。 具体示例: # -*- coding: UTF-8 -*- def sum(num1,num2): # 两数之和 if not (isinstance (num1,(int ,float)) and isinstance (

  • 问题内容: 我不想检查我的网址statusCode是否等于200,如果statusCode等于200,我创建了一个返回布尔值的函数,我使用的是dataTask,但我不知道如何返回值: 中的返回返回错误: void函数中非预期的非无效返回值 问题答案: 为了返回值,您应该使用块。尝试像这样声明您的功能: 然后这样称呼它: 希望这会有所帮助。

  • 问题内容: 我正在使用Postgresql 8.3,并具有以下简单功能,该功能会将a返回 给客户端 现在,我可以使用以下SQL命令来调用此函数并操纵返回的游标,但是游标名称是由PostgreSQL自动生成的 此外,如38.7.3.5中所述,显式地将游标名称声明为函数的输入参数 。返回游标。我可以声明自己的游标名称并使用此游标名称来操纵返回的游标,而不是为我自动生成的Postgresql吗?如果不是