Program Tip

C에서 함수를 호출하기 전의 매개 변수 평가 순서

programtip 2020. 11. 11. 20:35
반응형

C에서 함수를 호출하기 전의 매개 변수 평가 순서


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

}

아니요, 함수 매개 변수는 C에서 정의 된 순서로 평가되지 않습니다.

C ++ 프로그래머가 알아야 할 일반적인 정의되지 않은 동작은 무엇입니까?에 대한 Martin York의 답변을 참조하십시오 . .


함수 인수의 평가 순서는 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

경험을 추가하기 위해.
다음 코드 :

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

결과

2 1 3-Linux.i686에서 g ++ 4.2.1
1 2 3사용 -Linux.i686에서 SunStudio C ++ 5.9
2 1 3사용 -SunOS.x86pc에서 g ++ 4.2.1
1 2 3사용 -SunOS.x86pc에서 SunStudio C ++ 5.9
1 2 3사용 -SunOS.sun4u에서 g ++ 4.2.1
1 2 3사용-사용 SunOS.sun4u의 SunStudio C ++ 5.9


C에서 호출 할 때 함수 매개 변수의 평가 순서를 가정 할 수 있습니까?

,이 경우 아니, 그것은 가정 할 수 없는 동작초안 C99 표준 섹션의 6.5단락은 3말합니다 :

연산자와 피연산자의 그룹화는 구문으로 표시됩니다 .74) 나중에 지정된 경우를 제외하고 (함수 호출 (), &&, ||,? : 및 쉼표 연산자의 경우), 하위 표현식의 평가 순서 및 순서 어떤 부작용이 발생하는지는 둘 다 명시되지 않았습니다.

It also says except as specified later and specifically sites function-call (), so we see that later on the draft standard in section 6.5.2.2 Function calls paragraph 10 says:

The order of evaluation of the function designator, the actual arguments, and subexpressions within the actual arguments is unspecified, but there is a sequence point before the actual call.

This program also exhibits undefined behavior since you are modifying pa more than once between sequence points. From draft standard section 6.5 paragraph 2:

Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be read only to determine the value to be stored.

it cites the following code examples as being undefined:

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

Important to note that although the comma operator does introduce sequence points, the comma used in function calls is a separator and not the comma operator. If we look at section 6.5.17 Comma operator paragraph 2 says:

The left operand of a comma operator is evaluated as a void expression; there is a sequence point after its evaluation.

but paragraph 3 says:

EXAMPLE As indicated by the syntax, the comma operator (as described in this subclause) cannot appear in contexts where a comma is used to separate items in a list (such as arguments to functions or lists of initializers).

Without knowing this, having warnings turned on with gcc using at least -Wall would have provided a message similar to:

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

and by default clang will warn with a message similar to:

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

In general it is important to understand how to use your tools in the most effective way, getting to know the flags available for warnings is important, for gcc you can find that information here. Some flags that are useful and will save you a lot of trouble in the long run and are common to both gcc and clang are -Wextra -Wconversion -pedantic. For clang understanding -fsanitize can be very helpful. For example -fsanitize=undefined will catch many instances of undefined behavior at runtime.


As others already said, the order in which function arguments are evaluated is unspecified, and there is no sequence point between evaluating them. Because you change pa subsequently while passing each argument, you change and read pa twice in between two sequence points. That's actually undefined behavior. I found a very nice explanation in the GCC manual, which i think might be helpful:

The C and C++ standards defines the order in which expressions in a C/C++ program are evaluated in terms of sequence points, which represent a partial ordering between the execution of parts of the program: those executed before the sequence point, and those executed after it. These occur after the evaluation of a full expression (one which is not part of a larger expression), after the evaluation of the first operand of a &&, ||, ? : or , (comma) operator, before a function is called (but after the evaluation of its arguments and the expression denoting the called function), and in certain other places. Other than as expressed by the sequence point rules, the order of evaluation of subexpressions of an expression is not specified. All these rules describe only a partial order rather than a total order, since, for example, if two functions are called within one expression with no sequence point between them, the order in which the functions are called is not specified. However, the standards committee have ruled that function calls do not overlap.

It is not specified when between sequence points modifications to the values of objects take effect. Programs whose behavior depends on this have undefined behavior; the C and C++ standards specify that “Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be read only to determine the value to be stored.”. If a program breaks these rules, the results on any particular implementation are entirely unpredictable.

Examples of code with undefined behavior are a = a++;, a[n] = b[n++] and a[i++] = i;. Some more complicated cases are not diagnosed by this option, and it may give an occasional false positive result, but in general it has been found fairly effective at detecting this sort of problem in programs.

The standard is worded confusingly, therefore there is some debate over the precise meaning of the sequence point rules in subtle cases. Links to discussions of the problem, including proposed formal definitions, may be found on the GCC readings page, at http://gcc.gnu.org/readings.html.


Modifying a variable more than once in a expression is undefined behavior. So you might get different results on different compilers. So avoid modifying a variable more than once.


Grant's answer is correct, it's undefined.

BUT,,,

By your example, your compiler seems to be evaluating in right-to-left order (unsurprisingly, the order that arguments are pushed onto the stack). If you can do other tests to show that the order is maintained consistently even with optimizations enabled, and if you're only going to stick with that one version of the compiler, you can safely assume right-to-left ordering.

It's totally non-portable and a horrible, horrible thing to do, though.

참고URL : https://stackoverflow.com/questions/376278/parameter-evaluation-order-before-a-function-calling-in-c

반응형