문제

가능한 중복:
C, C++, Java 및 C#의 사전 및 사후 증분 연산자 동작

테스트 사례는 다음과 같습니다.


void foo(int i, int j)
{
   printf("%d %d", i, j);
}
...
test = 0;
foo(test++, test);

나는 "0 1"출력을 얻을 것으로 예상되지만 "0 0"을 얻을 수 있습니다.

도움이 되었습니까?

해결책

이는 지정되지 않은 동작의 예입니다.표준은 ~ 아니다 인수가 어떤 순서로 평가되어야 하는지 말해보세요.이는 컴파일러 구현 결정입니다.컴파일러는 함수에 대한 인수를 순서에 관계없이 자유롭게 평가할 수 있습니다.

이 경우 실제로 인수를 왼쪽에서 오른쪽으로 처리하는 대신 오른쪽에서 왼쪽으로 처리하는 것처럼 보입니다.

일반적으로 인수에서 부작용을 수행하는 것은 나쁜 프로그래밍 습관입니다.

대신에 foo(테스트++, 테스트); 너는 써야 해 foo(테스트, 테스트+1);테스트++;

이는 달성하려는 작업과 의미상 동일합니다.

편집하다:Anthony가 정확하게 지적했듯이, 중간 시퀀스 포인트 없이 단일 변수를 읽고 수정하는 것은 정의되지 않았습니다.따라서 이 경우의 행동은 실제로 한정되지 않은.따라서 컴파일러는 원하는 코드를 자유롭게 생성할 수 있습니다.

다른 팁

이것은 단지 불특정 행동, 실제로는 그렇다 정의되지 않은 동작 .

예, 인수 평가 순서는 다음과 같습니다. 불특정, 하지만 그것은 한정되지 않은 읽기가 새 값을 계산하기 위한 목적으로만 사용되지 않는 한 중간 시퀀스 포인트 없이 단일 변수를 읽고 수정합니다.함수 인수 평가 사이에는 순서 지점이 없으므로 f(test,test++) ~이다 정의되지 않은 동작: test 한 인수에 대해 읽고 다른 인수에 대해 수정 중입니다.수정 사항을 함수로 옮기면 괜찮습니다.

int preincrement(int* p)
{
    return ++(*p);
}

int test;
printf("%d %d\n",preincrement(&test),test);

진입과 퇴출에 시퀀스 포인트가 있기 때문입니다. preincrement, 이므로 호출은 단순 읽기 전이나 후에 평가되어야 합니다.이제 순서는 바로 불특정.

쉼표도 참고하세요 운영자 시퀀스 포인트를 제공하므로

int dummy;
dummy=test++,test;

괜찮습니다 --- 읽기 전에 증가가 발생하므로 dummy 새로운 값으로 설정됩니다.

내가 원래 말한 것은 모두 틀렸습니다!부작용이 계산되는 시점 ~이다 지정되지 않음.Visual C++에서는 test가 지역 변수인 경우 foo()를 호출한 후에 증가를 수행하지만 test가 static 또는 global로 선언된 경우 foo()를 호출하기 전에 증가하고 다른 결과를 생성합니다. 테스트가 정확할 것입니다.

증가는 실제로 foo() 호출 후 별도의 명령문에서 수행되어야 합니다.동작이 C/C++ 표준에 지정되어 있더라도 혼란스러울 것입니다.C++ 컴파일러가 이를 잠재적인 오류로 표시한다고 생각할 것입니다.

여기 시퀀스 포인트와 지정되지 않은 동작에 대한 좋은 설명입니다.

<----잘못된 잘못된 시작---->

"test++"의 "++" 비트는 foo를 호출한 후에 실행됩니다.따라서 (1,0)이 아닌 (0,0)을 foo에 전달합니다.

다음은 Visual Studio 2002의 어셈블러 출력입니다.

mov ecx, DWORD PTR _i$[ebp]
push    ecx
mov edx, DWORD PTR tv66[ebp]
push    edx
call    _foo
add esp, 8
mov eax, DWORD PTR _i$[ebp]
add eax, 1
mov DWORD PTR _i$[ebp], eax

증가는 foo() 호출 이후에 수행됩니다.이 동작은 의도적으로 설계된 것이지만 일반 독자에게는 혼란을 줄 수 있으므로 피해야 할 것입니다.증가는 실제로 foo() 호출 후 별도의 명령문에서 수행되어야 합니다.

<----끝이 틀렸어요 틀렸어요 ---->

"지정되지 않은 동작"이지만 실제로 C 호출 스택이 지정된 방식을 사용하면 거의 항상 0, 0으로 표시되고 결코 1, 0으로 표시되지 않습니다.

누군가 언급했듯이 VC의 어셈블러 출력은 스택에서 가장 오른쪽에 있는 매개변수를 먼저 푸시합니다.이것이 어셈블러에서 C 함수 호출이 구현되는 방식입니다.이는 C의 "끝없는 매개변수 목록" 기능을 수용하기 위한 것입니다.오른쪽에서 왼쪽 순서로 매개변수를 푸시하면 첫 번째 매개변수가 스택의 맨 위 항목이 되는 것이 보장됩니다.

printf의 서명을 받아보세요:

int printf(const char *format, ...);

해당 타원은 알 수 없는 매개변수 수를 나타냅니다.매개변수가 왼쪽에서 오른쪽으로 푸시되면 형식은 크기를 알 수 없는 스택의 맨 아래에 있게 됩니다.

C(및 C++)에서 매개변수가 왼쪽에서 오른쪽으로 처리된다는 점을 알면 함수 호출을 구문 분석하고 해석하는 가장 간단한 방법을 결정할 수 있습니다.매개변수 목록의 끝까지 가서 푸시를 시작하고 진행하면서 복잡한 문을 평가합니다.

그러나 대부분의 C 컴파일러에는 "파스칼 스타일" 함수를 구문 분석하는 옵션이 있으므로 이 방법으로도 문제를 해결할 수 없습니다.그리고 이것이 의미하는 바는 함수 매개변수가 왼쪽에서 오른쪽으로 스택에 푸시된다는 것입니다.예를 들어 printf가 Pascal 옵션으로 컴파일된 경우 출력은 대부분 1, 0이 될 것입니다(그러나 printf는 타원을 사용하기 때문에 Pascal 스타일로 컴파일될 수 없다고 생각합니다).

C는 함수 호출에서 매개변수 평가 순서를 보장하지 않으므로 "0 1" 또는 "0 0" 결과를 얻을 수 있습니다.순서는 컴파일러마다 바뀔 수 있으며, 동일한 컴파일러라도 최적화 매개변수에 따라 다른 순서를 선택할 수 있습니다.

foo(test, test + 1)을 작성하고 다음 줄에서 ++test를 수행하는 것이 더 안전합니다.어쨌든 컴파일러는 가능하다면 이를 최적화해야 합니다.

컴파일러는 예상한 순서대로 인수를 평가하지 않을 수 있습니다.

함수에 대한 인수의 평가 순서는 정의되지 않습니다.이 경우 오른쪽에서 왼쪽으로 수행한 것으로 보입니다.

(시퀀스 포인트 사이의 변수를 수정하면 기본적으로 컴파일러가 원하는 모든 작업을 수행할 수 있습니다.)

음, 이제 일관성을 위해 OP가 편집되었으므로 답변과 동기화되지 않습니다.평가 순서에 대한 근본적인 대답은 정확합니다.그러나 구체적인 가능한 값은 foo(++test, test);사례.

++테스트 ~ 할 것이다 전달되기 전에 증가하므로 첫 번째 인수는 항상 1입니다.두 번째 인수는 평가 순서에 따라 0 또는 1이 됩니다.

C 표준에 따르면 단일 시퀀스 포인트에서 변수에 대한 참조를 두 개 이상 갖는 것은 정의되지 않은 동작입니다(여기서는 이를 명령문 또는 함수에 대한 매개변수로 생각할 수 있음). 이러한 참조 중 하나 이상이 다음을 포함합니다. 사전/사후 수정.그래서:foo(f++,f) <--f가 증가하는 시점에 대해서는 정의되지 않았습니다.그리고 마찬가지로 (사용자 코드에서 항상 이것을 봅니다):*p = p++ + p;

일반적으로 컴파일러는 이러한 유형의 동작을 변경하지 않습니다(주요 개정 제외).

경고를 켜고 주의를 기울여 이를 방지하세요.

다른 사람들의 말을 반복하자면, 이는 지정되지 않은 동작이 아니라 오히려 정의되지 않은 동작입니다.이 프로그램은 합법적으로 무엇이든 출력하거나, n을 임의의 값으로 남겨두거나, 상사에게 모욕적인 이메일을 보낼 수 있습니다.

실제로 컴파일러 작성자는 일반적으로 작성하기 가장 쉬운 작업을 수행합니다. 이는 일반적으로 프로그램이 n을 한두 번 가져오고 함수를 호출하고 언젠가 증가한다는 것을 의미합니다.이는 다른 가능한 동작과 마찬가지로 표준에 따르면 괜찮습니다.컴파일러나 버전 간에 또는 다른 컴파일러 옵션을 사용하여 동일한 동작을 기대할 이유가 없습니다.동일한 프로그램에서 서로 다르지만 비슷해 보이는 두 가지 예제를 일관되게 컴파일해야 할 이유는 없습니다. 물론 그럴 것이라고 생각합니다.

한마디로 이렇게 하지 마세요.궁금하다면 다양한 상황에서 테스트해 보세요. 하지만 단 하나의 정확하거나 심지어 예측 가능한 결과가 있는 척하지 마세요.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top