当前位置:  开发笔记 > 前端 > 正文

在C中调用函数之前的参数评估顺序

如何解决《在C中调用函数之前的参数评估顺序》经验,为你挑选了5个好方法。

在C中调用时,可以假设函数参数的评估顺序吗?根据以下程序,我执行时似乎没有特定的顺序.

#include 

int main()
{
   int a[] = {1, 2, 3};
   int * pa; 

   pa = &a[0];
   printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa), *(pa++),*(++pa));
   /* Result: a[0] = 3  a[1] = 2    a[2] = 2 */

   pa = &a[0];
   printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa++),*(pa),*(++pa));
   /* Result: a[0] = 2  a[1] = 2     a[2] = 2 */

   pa = &a[0];
   printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa++),*(++pa), *(pa));
   /* a[0] = 2  a[1] = 2 a[2] = 1 */

}

Grant Wagner.. 63

不,函数参数不会在C中按照定义的顺序进行评估.

请参阅Martin York的回答c ++程序员应该了解的所有常见的未定义行为是什么?.



1> Grant Wagner..:

不,函数参数不会在C中按照定义的顺序进行评估.

请参阅Martin York的回答c ++程序员应该了解的所有常见的未定义行为是什么?.


这不是真的令人不安.如果定义了评估顺序,那么您将会有一些C/C++编译器生成不太理想的代码.例如,如果args从后向前推入堆栈,那么从前到后对它们进行评估意味着更多的临时存储空间可以使呼叫正确.
这是如此令人不安但却是如此真实
我认为C调用约定要求args被推回到前面,让param#0始终作为堆栈中的第一项.评估的顺序没有定义,但最简单的方法是循环:"Eval-Push-Repeat",从右向左移动.

2> Robert Gambl..:

函数参数的评估顺序未指定,来自C99§6.5.2.2p10:

函数指示符的评估顺序,实际参数和实际参数中的子表达式是未指定的,但在实际调用之前有一个序列点.

C89中存在类似的措辞.

此外,您正在修改pa多次而没有插入调用未定义行为的序列点(逗号运算符引入了一个序列点,但是逗号分隔函数参数没有).如果你打开编译器上的警告,它应该警告你:

$ gcc -Wall -W -ansi -pedantic test.c -o test
test.c: In function ‘main’:
test.c:9: warning: operation on ‘pa’ may be undefined
test.c:9: warning: operation on ‘pa’ may be undefined
test.c:13: warning: operation on ‘pa’ may be undefined
test.c:13: warning: operation on ‘pa’ may be undefined
test.c:17: warning: operation on ‘pa’ may be undefined
test.c:17: warning: operation on ‘pa’ may be undefined
test.c:20: warning: control reaches end of non-void function



3> 小智..:

只是为了增加一些经验.
以下代码:

int i=1;
printf("%d %d %d\n", i++, i++, i);

结果是

2 1 3- 在Linux.i686上使用g ++ 4.2.1 - 在Linux.i686上
1 2 3使用SunStudio C++ 5.9 - 在SunOS.x86pc上
2 1 3使用g ++ 4.2.1 - 在SunOS.x86pc上
1 2 3使用SunStudio C++ 5.9 - 在SunOS.sun4u上
1 2 3使用g ++ 4.2.1
1 2 3- 使用SunOS C++ 5.9在SunOS.sun4u上


虽然这些可能是有效的观察,但这里没有实际答案.

4> Shafik Yaghm..:

在C中调用时,可以假设函数参数的评估顺序吗?

不,不能假设,如果是未指明的行为,则CII标准草案中的段落6.5段落3说:

运算符和操作数的分组由语法表示.74)除了稍后指定的(对于函数调用(),&&,||,?:和逗号运算符),子表达式的评估顺序和顺序发生哪些副作用都是未指明的.

除了后面指定的以及特定的站点之外,它还说明了function-call (),所以我们在后面的6.5.2.2 函数调用段落中的草案标准中看到10:

功能指示符,实际的论点,并且实际的参数中的子表达式的计算顺序是不确定的,但实际调用之前的顺序点.

由于您在序列点之间进行了多次修改,因此该程序还会显示未定义的行为.从草案标准部分段落:pa6.52

在前一个和下一个序列点之间,对象的存储值最多只能通过表达式的计算来修改一次.此外,先前的值应该只读以确定要存储的值.

它引用以下代码示例为未定义:

i = ++i + 1;
a[i++] = i; 

需要注意的是,虽然逗号运算符确实引入了序列点,但函数调用中使用的逗号是分隔符,而不是comma operator.如果我们看一下6.5.17 逗号运算符段落2说:

逗号运算符的左操作数被计算为void表达式; 评估后有一个序列点.

但段落3说:

示例如语法所示,逗号运算符(如本子条款中所述)不能出现在使用逗号分隔列表中的项目的上下文中(例如函数的参数或初始化程序列表).

不知道这一点,gcc至少使用警告打开-Wall会提供类似于以下内容的消息:

warning: operation on 'pa' may be undefined [-Wsequence-point]
printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa), *(pa++),*(++pa));
                                                            ^

默认情况下clang会发出类似于以下消息的警告:

warning: unsequenced modification and access to 'pa' [-Wunsequenced]
printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa), *(pa++),*(++pa));
                                            ~         ^

一般来说,了解如何以最有效的方式使用工具非常重要,了解可用于警告的标志非常重要,因为gcc您可以在此处找到相关信息.有些标志是有用的,会为你节省很多的麻烦,从长远来看并是共同的gccclang-Wextra -Wconversion -pedantic.为了clang 理解-fsanitize可以是非常有帮助的.例如,-fsanitize=undefined将在运行时捕获许多未定义行为的实例.



5> Johannes Sch..:

正如其他人已经说过的那样,评估函数参数的顺序是未指定的,并且在评估它们之间没有序列点.因为您pa在传递每个参数时随后更改pa,所以在两个序列点之间更改并读取两次.这实际上是未定义的行为.我在GCC手册中找到了一个非常好的解释,我认为这可能会有所帮助:

C和C++标准定义了C/C++程序中表达式按顺序点进行计算的顺序,它表示程序各部分执行之间的部分顺序:在序列点之前执行的顺序和执行之后执行的顺序它.在评估了&&,||,?的第一个操作数之后,在完整表达式(一个不是较大表达式的一部分)的评估之后发生了这些.:或者,(逗号)运算符,在调用函数之前(但在评估其参数和表示被调用函数的表达式之后),以及某些其他地方.除了由序列点规则表示之外,未指定表达式的子表达式的评估顺序.所有这些规则仅描述部分订单而不是总订单,例如,如果在一个表达式中调用两个函数且它们之间没有序列点,则不指定调用函数的顺序.但是,标准委员会已经裁定函数调用不重叠.

在序列点之间没有指定对对象值的修改生效.行为依赖于此的程序具有未定义的行为; C和C++标准规定"在上一个和下一个序列点之间,一个对象的表达式的评估最多只能修改一次存储值.此外,先前的值应该是只读的,以确定要存储的值." 如果程序违反了这些规则,那么任何特定实现的结果都是完全不可预测的.

具有未定义行为的代码示例是a = a ++ ;, a [n] = b [n ++]和[i ++] = i;.一些更复杂的病例没有被这个选项诊断出来,并且它可能偶尔会出现假阳性结果,但一般来说,它已被发现在检测程序中的这类问题时相当有效.

该标准令人困惑,因此在细微的情况下对序列点规则的确切含义存在争议.有关问题讨论的链接,包括拟议的正式定义,可在GCC阅读页面http://gcc.gnu.org/readings.html上找到.

推荐阅读
360691894_8a5c48
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有