IT이야기

C에서 함수를 호출하기 전의 파라미터 평가 순서

cyworld 2022. 5. 20. 21:40
반응형

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++ 프로그래머가 알아야일반적인 정의되지 않은 행동은 모두 무엇인가에 대한 마틴 요크의 답변을 참조하십시오.

그냥 경험을 더하기 위해서.
다음 코드:

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

결과를 낳다

2 1 3 Linux.i686에서 4
1 2 3 Linux.i686에서 5 사용
2 1 3 SunOS.x86pc에서 4
1 2 3 C 사용
1 2 3에서 g 4.Sun4u에서 g++ 4.2.1 사용
1 2 3 5.Sun4uaeo SunStudio C++ 5.9 사용

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

아니, 지정되지 않은 행동인 경우 섹션의 C99 표준 초안을 가정할 수 없다.6.5 단락3다음과 같이 말한다.

연산자와 피연산자의 그룹화는 구문.74)로 표시된다. 단, 나중(함수 호출)(&&), |||, ?:, 콤마 연산자에 대해 지정되는 것을 제외하고, 하위 표현에 대한 평가 순서와 부작용이 발생하는 순서는 모두 불특정하다.

또한 지정된 나중 및 특정 사이트를 제외하고도 제외한다고 명시되어 있다.function-call (), 그래서 우리는 나중에 섹션의 초안 표준에서 그것을 본다.6.5.2.2 함수 호출 단락10다음과 같이 말한다.

함수 지정자의 평가 순서, 실제 인수, 실제 인수 에서의 하위 표현 등은 불특정하지만 실제 통화 전에 시퀀스 포인트가 있다.

이 프로그램은 수정 중이므로 정의되지 않은 동작도 나타남pa시퀀스 포인트 사이에 두 번 이상초안 표준 섹션부터6.5 단락2:

이전과 다음 시퀀스 지점 사이에서 개체는 표현식의 평가에 의해 최대번에 저장된 값을 수정해야 한다.또한 저장될 값을 결정하기 위해 이전 값을 읽어야 한다.

정의되지 않은 것으로 다음과 같은 코드 예를 인용한다.

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

중요한 점은 쉼표 연산자가 시퀀스 포인트를 도입하지만 함수 호출에 사용되는 쉼표는 구분 기호가 아니라comma operator 만약 가 이 부분을 .6.5.17 쉼표 연산자 단락2다음과 같이 말한다.

쉼표 연산자의 왼쪽 피연산자는 보이드 표현식으로 평가된다. 평가시퀀스 포인트가 있다.

단락 단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당신은 여기서 그 정보를 찾을 수 있다.어떤 깃발은 유용하고 장기적으로 많은 문제를 덜어줄 것이며 두 가지 모두에 공통적인 것이다.gcc그리고clang이다-Wextra -Wconversion -pedantic.을 위해clang -fsanitize를 이해하는 것은 매우 도움이 될 수 있다.예를 들면-fsanitize=undefined런타임에 정의되지 않은 행동의 많은 예를 포착할 것이다.

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

다른 사람들이 이미 말했듯이 함수 인수를 평가하는 순서는 불특정하며, 함수 인수를 평가하는 순서 사이에는 순서가 없다.네가 바뀌기 때문에pa이후 각 주장을 전달하는 동안, 당신은 변화하고 읽음pa두 개의 시퀀스 포인트 사이에 두 번그건 사실 정의되지 않은 행동이야.GCC 매뉴얼에서 아주 멋진 설명을 찾아냈는데, 이 설명이 도움이 될 것 같다.

C와 C++ 표준은 C/C++ 프로그램의 표현이 시퀀스 포인트 단위로 평가되는 순서를 정의하는데, 이는 프로그램의 일부 실행 부분, 즉 시퀀스 포인트 이전에 실행된 부분과 이후에 실행된 부분 사이의 부분적인 순서를 나타낸다.이러한 것들은 전체 표현식(더 큰 표현식의 일부가 아닌 표현식)의 평가 후에, &&, ||, ? : 또는 (콤마) 연산자의 첫 번째 피연산자에 대한 평가 후에, 어떤 함수를 호출하기 전에(그러나 그 주장과 호출된 함수를 나타내는 표현식의 평가 후에), 그리고 특정한 다른 장소에서 발생한다.시퀀스 포인트 규칙에 의해 표현되는 것 외에, 표현식의 하위 표현에 대한 평가 순서는 명시되지 않는다.예를 들어, 두 함수를 시퀀스 포인트가 없는 한 표현식 내에서 호출할 경우 함수 호출 순서가 지정되지 않기 때문에 이 모든 규칙은 전체 순서보다는 부분 순서만 설명한다.그러나 표준위원회는 기능 호출이 중복되지 않는다고 판정했다.

물체 값에 대한 시퀀스 포인트 간 수정 사항이 적용되는 경우에는 지정되지 않는다.이에 따라 동작이 달라지는 프로그램들은 정의되지 않은 동작을 가지고 있다; C와 C++ 표준은 "이전 및 다음 시퀀스 지점들 사이에서 개체는 표현식의 평가에 의해 최대 한 번에 그 저장 값을 수정해야 한다"고 명시한다.또한 이전 값은 저장될 값을 결정하기 위해 읽어야 한다.프로그램이 이러한 규칙을 어긴다면, 어떤 특정한 구현에 대한 결과는 완전히 예측할 수 없다.

정의되지 않은 동작이 있는 코드의 예로는 a = a++;, a[n] = b[n++], a[i++] = i; 등이 있다.더 복잡한 몇몇 사례들은 이 선택사항으로 진단되지 않고, 그것은 때때로 거짓 양성 결과를 줄 수도 있지만, 일반적으로 그것은 프로그램에서 이러한 종류의 문제를 감지하는 데 상당히 효과적인 것으로 밝혀졌다.

표준은 헷갈리게 단어화되어 있으므로 미묘한 경우 시퀀스 포인트 규칙의 정확한 의미에 대해 약간의 논쟁이 있다.제안된 공식 정의를 포함한 문제의 토론에 대한 링크는 GCC 판독 페이지(http://gcc.gnu.org/readings.html에서 찾을 수 있다.

그랜트의 대답은 정확해, 그건 정의가 없어.

그렇지만

당신의 예에 따르면, 당신의 컴파일러는 오른쪽에서 왼쪽 순서로 평가하는 것 같다(놀랍지 않게, 인수가 스택으로 밀려오는 순서).최적화가 활성화된 상태에서도 순서가 일관성 있게 유지된다는 것을 보여주기 위해 다른 테스트를 수행할 수 있고, 컴파일러의 한 버전만 고수할 경우 오른쪽에서 왼쪽으로 순서를 안전하게 가정할 수 있다.

하지만 이건 완전히 휴대할 수 없고 끔찍하고 끔찍한 일이야.

식에서 변수를 두 번 이상 수정하는 것은 정의되지 않은 동작이다.그래서 당신은 컴파일러에 따라 다른 결과를 얻을 수도 있다.따라서 변수를 두 번 이상 수정하지 마십시오.

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

반응형