C语言中函数调用前的参数求值顺序

8cdiaqws  于 2022-12-03  发布在  其他
关注(0)|答案(7)|浏览(178)

在C语言中调用函数时,是否可以假定函数参数的求值顺序?根据下面的程序,我执行它时似乎没有特定的顺序。

#include <stdio.h>

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 */

}
zzzyeukh

zzzyeukh1#

不,函数参数在C中不按定义的顺序求值。
请参阅Martin约克对What are all the common undefined behaviour that c++ programmer should know about?的回答。

xdnvmnnf

xdnvmnnf2#

函数参数的求值顺序未指定,参见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
t3psigkw

t3psigkw3#

只是为了增加一些经验。
下面的代码:

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

导致
在Linux上使用g++ 4.2.1。i686
1 2 3-在Linux上使用SunStudio C++ 5.9。i686
2 1 3-在SunOS上使用g++ 4.2.1。x86 pc
1 2 3-在SunOS上使用SunStudio C++ 5.9。x86 pc
1 2 3-在SunOS上使用g++ 4.2.1。
1 2 3-在操作系统上使用C++ 5.9。

m4pnthwp

m4pnthwp4#

在C中调用函数时,是否可以假定函数参数的求值顺序?
不,如果是unspecified behavior,则不能假定,6.5部分3中的draft C99 standard表示:
运算符和操作数的分组由语法指示。74)除了后面指定的(对于函数调用(),&&,||、?:和逗号运算子),未指定子运算式的评估顺序和副作用发生的顺序。
它还说,除了后面特别指定的站点function-call (),所以我们看到,稍后在标准草案6.5.2.2Function calls10部分中,段落10说:

未指定函数指示符、实际参数和实际参数中的子表达式的求值顺序,但在实际调用之前有一个序列点。

此程序还显示undefined behavior,因为您在sequence points之间多次修改pa。根据草案标准第6.5节第2段:

在上一个和下一个序列点之间,对象的存储值最多只能通过表达式求值修改一次。此外,前一个值应为只读,以确定要存储的值。

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

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

值得注意的是,虽然comma operator确实引入了序列点,但函数调用中使用的逗号是分隔符而不是comma operator。如果我们看6.5.17节 * 逗号运算符 * 2段,可以看出:
逗号运算符的左操作数计算为void表达式;在其求值之后存在序列点。
但是第3段说:
示例如语法所示,逗号运算符(如本子条款中所述)不能出现在使用逗号分隔列表中项目的上下文中(如函数的参数或初始化程序列表)。
如果不知道这一点,则至少使用-Wall打开gcc警告将提供类似于以下内容的消息:

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,您可以找到here的信息。一些有用的标志,从长远来看可以为您节省很多麻烦,并且是gccclang所共有的标志是-Wextra -Wconversion -pedantic。对于clang,理解-fsanitize非常有帮助。例如,-fsanitize=undefined将在运行时捕获许多未定义行为的示例。

sxissh06

sxissh065#

正如其他人已经说过的,函数参数的求值顺序是未指定的,并且求值之间没有序列点。因为在传递每个参数时,你随后更改了pa,所以在两个序列点之间更改和读取了pa两次。这实际上是未定义的行为。我在愚者手册中找到了一个非常好的解释,我认为这可能会有帮助:
C和C标准根据序列点定义了C/C程序中表达式的求值顺序,序列点表示程序各部分执行之间的偏序:在序列点之前执行的那些,以及在序列点之后执行的那些。这些发生在完整表达式(不是更大表达式的一部分的表达式)的求值之后,在&&的第一个操作数的求值之后,||,?:或者,(逗号)运算符,在调用函数之前(但是在它的参数和表示被调用函数的表达式求值之后),以及在某些其他地方。除了由序列点规则表示的,表达式的子表达式求值的顺序是不指定的。所有这些规则只描述了部分顺序而不是全部顺序,因为,例如,如果两个函数在一个表达式中被调用,而它们之间没有序列点,那么函数被调用的顺序是不指定的。2但是,标准委员会已经规定函数调用不能重叠。
在序列点之间对对象值的修改何时生效没有指定。其行为依赖于此的程序具有未定义的行为; C和C标准规定“在前一个和下一个序列点之间,对象的存储值最多只能通过表达式的求值修改一次。此外,前一个值只能被读取以确定要存储的值。"如果程序违反了这些规则,则任何特定实现的结果都是完全不可预测的。
具有未定义行为的代码的示例是a = a
; a[n] = B[n++]且a[i++] = i;一些更复杂的情况不能通过该选项诊断,并且它可能给予偶然的假阳性结果,但是通常已经发现它在检测程序中的这类问题方面相当有效。
该标准的措辞令人困惑,因此在一些微妙的情况下,对于序列点规则的确切含义存在一些争论。有关该问题的讨论,包括提出的正式定义,可以在愚者阅读页面上找到链接,地址为http://gcc.gnu.org/readings.html

2q5ifsrm

2q5ifsrm6#

在表达式中多次修改变量是未定义的行为。因此,在不同的编译器上可能会得到不同的结果。因此,请避免多次修改变量。

2hh7jdfx

2hh7jdfx7#

格兰特的答案是正确的,它是未定义的。
但是...
从你的例子来看,你的编译器似乎是按照从右到左的顺序进行计算的(毫不奇怪,就是把参数压入堆栈的顺序)。如果你可以做其他测试来证明即使启用了优化,这个顺序也是一致的,并且如果你只打算坚持使用那个版本的编译器,你就可以安全地假设从右到左的顺序。
这是完全不可移植的,一个可怕的,可怕的事情做,虽然。

相关问题