【问题标题】:Order of parameter evaluation of function call in GCCGCC中函数调用的参数评估顺序
【发布时间】:2013-06-04 16:30:24
【问题描述】:

当我用谷歌搜索这个时,我总是得到关于评估顺序的线程,一般来说,评估顺序是未指定的。

我知道参数评估顺序在C中一般是未指定的。

我的问题是gcc中的参数评估顺序,从左到右还是从右到左?

任何资源链接也将不胜感激......

编辑:消除问题中的歧义

嗯,我说的是当时的情况

       foo(1+2,2+3,4+9)

首先评估哪个?

1+2 还是 4+9... 一样明智..

我们可以通过在一个 gcc 编译器中编译它来进行声明吗....?还是在不同的 gcc 版本之间也不同?

【问题讨论】:

标签: c operator-precedence


【解决方案1】:

如果您真的要问foo(f1(), f2(), f3()) - 这比foo(1+2, 3+4, 5+6) 更有趣,因为添加 1+2 和 3+4 无论是先完成还是最后完成还是以随机顺序完成都不会产生效果。

现在,不幸的是,您不能依赖以任何特定顺序调用 f1()f2()f3() - 只要每个函数都被调用一次,顺序是任何顺序都可以:

   f1, f2, f3
   f1, f3, f2
   f2, f3, f1
   f2, f1, f3
   f3, f2, f1
   f3, f1, f2

(涵盖三个参数的所有排列)。

这完全取决于它“认为最好”的编译器。

很久以前我写了一些代码,然后遇到了这个特殊的问题 - 我遇到了类似的问题:

char foo(char a, char b)
 ... 
 if (a =! 'x')
   foo(fgetc(f), foo(fgetc(f))); 
 ...

由于我希望首先调用第一个(左)fgetc(),然后调用第二个fgetc(),因此我可以获得正确的行为。它在学校计算机上运行良好。然后我把代码带回家并尝试在我的家用电脑上使用它。由于某种原因,它不能正常工作。我花了很长时间才弄清楚foo() 只是被无限调用,因为a 从来都不是'x',这会停止递归——因为'x' 永远不会出现在第二次调用中。

那是在两台机器上使用 gcc,但一台是 sparc(学校计算机),而家里的一台是 x86(386,运行 OS/2,那是很久以前的事了)。

解决方法是将其分成几行:

 char aa = fgetc(f);
 char bb = fgetc(f);
 foo(aa, foo(bb)); 

【讨论】:

  • 我想知道架构因素是否也会影响编译器在这类问题中的设计.... gcc 的设计者/实施者是否让 gcc 标准在某处可用于这类问题问题?
  • 不同的事情可能会在其中发挥作用。例如,如果 f1() 是内联的,它可能会影响它需要哪些寄存器,因此它可能会受益于在调用 f2() 和 f3() 之前完成,反之亦然。 C++ 标准本身清楚地表明“这取决于编译器,它不必每次都以相同的方式工作”。 [如果您编译相同的源代码,使用相同的编译器设置等,它可能会这样做,但是如果您更改优化级别,它可能会决定以相反的方式对其进行洗牌,因为它现在内联 f2(),并且更改了寄存器用法 -> 不同的顺序更好。
  • 你不认为,它在第一个 foo 中的未定义行为,因为 fgetc(f) 是两次。如果是,则此行为不代表正确的机制,但在第二个 foo(solution) 中,行为被删除,所以它的工作方式相同,总是从左到右。
  • 我发布的代码与实际代码相比要简化得多,但未定义的行为来自两个不同的函数,它们对同一资源有副作用——在这种情况下,位置被读取文件 - 并依赖于以特定顺序调用的这两个函数。
【解决方案2】:
#include <stdio.h>

int f1(void){
    printf("In F1\n");
    return 0;
}

int f2(void){
    printf("In F2\n");
    return 0;
}
int f3(void ){
    printf("In F3\n");
    return 0;
}
void f4(int a,int b,int c){
    printf("In F4\n");
    return;
}


int main(){

f4(f1(),f2(),f3());

  getch();
return 0; 
}



output
----------
In F3
In F2
In F1
In F4


but for 
printf("%d..%d..%d",(f1(),f2(),f3()));

output
---------
In F1
In F2
In F3
0..0..0

【讨论】:

    【解决方案3】:

    C++ 中没有从左到右或从右到左求值的概念,不要将其与运算符的从左到右和从右到左结合性混淆:表达式 f1() + f2() + f3() 被解析为 (f1() + f2()) + f3() 由于 operator+ 的从左到右的关联性,但是对 f3 的函数调用可能首先被评估,最后,或之间f1() 或 f2() 在运行时。

    【讨论】:

    • 我说的是 c,我认为你的问题是错误的......或者我的问题是错误的,请参阅我在问题中的编辑
    【解决方案4】:

    在C中,参数传递机制是基于堆栈的 方法 它使用堆栈(称为:函数堆栈框架)来存储参数,因此顺序是正确的,因为堆栈是 LIFO 。这取决于我的理解。 请查看此链接,它将为您解释更多,希望您能找到一些有用的东西 http://zoo.cs.yale.edu/classes/cs427/2012a/resources/Chapter_05.pdf

    pg#55 ,5.2.3 参数传递机制

    【讨论】:

    • 您希望完全以社区 wiki 的形式发帖吗?还是偶然的还是不知不觉的? :)
    • 完全在不知不觉中......我在元堆栈上读到过它......我现在更加困惑
    • 社区回答不会为您赢得赞成/接受/反对票的声誉......任何人都可以编辑它们
    • 进入实际问题,虽然你的推理很好,但事实并非如此......请参阅@MatsPetersson 对这个问题的回答
    • 谢谢,这是我个人资料中的一项新功能。所以我很幸运,你告诉我
    【解决方案5】:

    我认为您将两个概念混为一谈:1)参数评估的顺序(在函数调用中)和参数推送的顺序。首先,未指定(取决于实现)在传递给函数之前评估哪些顺序参数。其次,它取决于函数的调用约定,cdecl 从右到左推送参数,几乎所有其他约定(例如,pascal)从左到右推送。

    【讨论】:

    • 好吧,我正在讨论函数调用
    • 那么最多可以说或依赖的就是订单未定义。它甚至可能从一个版本的 gcc 更改为下一个版本。
    • 求值顺序是未指定,不是未定义;它们是有区别的。只要不超过一个表达式对给定对象产生副作用,则行为是明确定义的。 foo(i++,j++,k++) 具有明确定义的行为,即使未指定评估和更新 ijk 的顺序。 foo(i++,i++,i++),OTOH,调用未定义的行为。
    • @JohnBode 是的,欣赏准确的语言。
    • @JohnBode 你的评论让人大开眼界
    【解决方案6】:
    // according to my tests: left to right for clang-3.4 and right to left
    // for gcc-4.8.2
    // but don't write any code relying on that: it would be highly unportable
    
    #include <stdio.h>
    
    int one() {
      printf("1\n");
      fflush(stdout);
      return 1;
    }
    
    int two() {
      printf("2\n");
      fflush(stdout);
      return 1;
    }
    
    int three() {
      printf("3\n");
      fflush(stdout);
      return 1;
    }
    
    void test(int a, int b, int c) {
      return;
    }
    
    int main() {
      test(one(), two(), three());
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-11-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-15
      • 2012-02-12
      相关资源
      最近更新 更多