programing

이것이 정의되지 않은 C 동작입니까?

instargram 2023. 10. 29. 19:00
반응형

이것이 정의되지 않은 C 동작입니까?

우리 수업은 C programming 교수로부터 다음과 같은 질문을 받았습니다.

코드는 다음과 같습니다.

int x=1;
printf("%d",++x,x+1);

항상 어떤 출력을 낼 수 있습니까?

대부분의 학생들은 명확하지 않은 행동을 했습니다.왜 그런지 이해하는 것을 도와줄 수 있는 사람?

편집과 답변 감사합니다만, 여전히 혼란스럽습니다.

출력은 모든 합당한 경우에 2가 될 가능성이 높습니다.사실, 당신이 가지고 있는 것은 명확하지 않은 행동입니다.

구체적으로, 표준은 다음과 같이 말합니다.

이전 시퀀스 포인트와 다음 시퀀스 포인트 사이에 객체는 식의 평가를 통해 최대 한 번까지 저장된 값을 수정해야 합니다.또한 저장할 값을 결정하기 위해서만 사전 값을 읽어야 합니다.

함수에 대한 인수를 평가하기 에 시퀀스 포인트가 있고, 모든 인수를 평가한 에 시퀀스 포인트가 있습니다(그러나 함수는 아직 호출되지 않았습니다).그 둘 사이에는 (즉, 인수가 평가되는 동안) 시퀀스 포인트가 없습니다 (인수가 표현이 내부적으로 포함되지 않는 한).&& ||아니면,연산자).

그 말은 바로 다음과 같은 전화를.printf는 저장되는 값을 결정하기 위해 둘 이전 값을 읽습니다(즉,++x) 및 번째 인수의 값을 결정합니다(즉,x+1되지 않은 이는 명백하게 위에서 인용한 요건을 위반하여 정의되지 않은 행위를 야기합니다.

변환 지정자가 지정되지 않은 추가 인수를 제공했다고 해서 정의되지 않은 동작이 발생하지는 않습니다.변환 지정자가 제공하는 인수 적거나, (촉진된) 인수 유형이 변환 지정자의 인수 유형과 일치하지 않으면 정의되지 않은 동작을 얻을 수 있지만, 추가 매개 변수를 전달하는 것은 그렇지 않습니다.

프로그램의 동작이 정의되지 않을 때마다, 어떤 일도 일어날 수 있습니다. 고전적인 문구는 "악마가 코 밖으로 날아갈 수도 있다"입니다. 대부분의 구현이 그렇게 멀리까지 진행되지는 않지만 말입니다.

함수의 인수는 개념적으로 병렬적으로 평가됩니다(기술 용어는 평가 사이에 순서점이 없다는 것입니다).그 말은 그 표현들이++x그리고.x+1이 순서, 반대 순서 또는 인터리브 방식으로 평가할 수 있습니다.변수를 수정하고 변수 값에 병렬로 액세스하려고 하면 동작이 정의되지 않습니다.

많은 구현에서 인수는 (항상 왼쪽에서 오른쪽으로) 순서대로 평가됩니다.그래서 현실 세계에서 두 개밖에 볼 수 없을 것 같습니다.

그러나 컴파일러는 다음과 같은 코드를 생성할 수 있습니다.

  1. 레지스터에 x 로드r1.
  2. 계산한다.x+1에 1을 더해서r1.
  3. 계산한다.++x에 1을 더해서r1. 괜찮아요 왜냐면x로 로드되었습니다.r1. 컴파일러가 어떻게 설계되었는지를 고려할 때, 2단계는 수정될 수 없습니다.r1, 왜냐하면 그것은 오직 만일에 일어날 수 있기 때문입니다.x두 시퀀스 포인트 사이에서 읽었을 뿐만 아니라 쓰였을 뿐만.그것은 C 표준에 의해 금지된 것입니다.
  4. r1안으로x.

그리고 이 (가정적이지만 정확한) 컴파일러에 프로그램은 3을 인쇄할 것입니다.

(편집: 추가적인 주장을 전달합니다.printf정확합니다(N1256의 §7.19.6.1-2; Prasoon Saurav 덕분에).또한: 예제를 추가했습니다.)

정답은 코드가 정의되지 않은 동작을 생성한다는 것입니다.

행동이 정의되지 않은 이유는 두 표현이++x그리고.x + 1수정 중입니다.x독서와 독서x(수정과 관련이 없는) 이유로, 이 두 동작은 시퀀스 포인트로 분리되지 않습니다.이로 인해 C (및 C++)에서 정의되지 않은 동작이 발생합니다.요구사항은 C 언어 표준의 6.5/2에 제시되어 있습니다.

주의할 점은, 이 사건의 정의되지 않은 행위는printf함수에는 형식 지정자 한 개와 실제 인수 두 개만 주어집니다.더 많은 주장을 하기 위해printf형식 문자열에 형식 지정자가 있기 때문에 C에서는 완벽하게 합법적입니다.다시 말하지만, 문제는 C 언어의 표현 평가 요건 위반에 뿌리를 두고 있습니다.

또한 이 토론의 일부 참가자들은 정의되지 않은 행동의 개념을 파악하지 못하고, 이를 불특정 행동의 개념과 혼합해야 한다고 주장합니다.차이점을 더 잘 설명하기 위해 다음과 같은 간단한 예를 생각해 보겠습니다.

int inc_x(int *x) { return ++*x; }
int x_plus_1(int x) { return x + 1; }

int x = 1;
printf("%d", inc_x(&x), x_plus_1(x));

위 코드는 원래 코드와 "동등한" 코드입니다. 단, 우리의 코드와 관련된 작업은 제외합니다.x기능으로 감싸집니다.이 최신 예에서는 어떤 일이 벌어질까요?

이 코드에는 정의되지 않은 동작이 없습니다.하지만 평가순서 이래로printf인수가 지정되지 않았습니다. 이 코드는 지정되지 않은 동작을 생성합니다. 즉, 다음이 가능합니다.printf로 불리게 됩니다printf("%d", 2, 2)또는 로서printf("%d", 2, 3). 두 경우 모두 출력이 실제로2. 그러나 이 변형의 중요한 차이점은 모든 접근이x각 함수의 시작과 끝에 있는 시퀀스 포인트로 감싸므로 이 변형으로 인해 정의되지 않은 동작이 발생하지 않습니다.

이것이 바로 다른 포스터들이 원래의 예를 강요하려는 이유입니다.하지만 할 수 없는 일입니다.원래의 예는 완전히 다른 짐승인 정의되지 않은 행동을 만들어냅니다.그들은 실제로 정의되지 않은 행동은 항상 불특정 행동과 동일하다고 주장하려고 하는 것 같습니다.이것은 그것을 만드는 사람들의 전문 지식의 부족을 보여줄 뿐인 완전히 가짜 주장입니다.원래 코드는 정의되지 않은 동작, 주기를 생성합니다.

예제를 계속하려면 이전 코드 샘플을 다음과 같이 수정합니다.

printf("%d %d", inc_x(&x), x_plus_1(x));

코드의 출력은 일반적으로 예측할 수 없게 됩니다.프린트가 가능합니다.2 2인쇄할 수도 있고,2 3. 그러나 동작이 예측 불가능하더라도 정의되지 않은 동작을 생성하지는 않습니다.동작이 지정되지 않았으며, 비트가 정의되지 않았습니다.지정되지 않은 동작은 다음 두 가지 가능성으로 제한됩니다.2 2아니면2 3되지 않은 은 어떤 정의되지 않은 동작은 어떤 것으로도 제한되지 않습니다.인쇄하는 대신 하드 드라이브를 포맷할 수 있습니다.차이를 느껴보라.

대부분의 학생들은 명확하지 않은 행동을 했습니다.왜 그런지 이해하는 것을 도와줄 수 있는 사람?

함수 모수가 계산되는 순서가 지정되어 있지 않기 때문입니다.

항상 어떤 출력을 낼 수 있습니까?

제가 생각하는 모든 환경에서 2개를 만들어 낼 것입니다.그러나 C99 표준을 엄격하게 해석하면 x에 대한 액세스가 시퀀스 포인트 간에 존재하는 요구 사항을 충족하지 못하기 때문에 동작이 정의되지 않습니다.

대부분의 학생들은 명확하지 않은 행동을 했습니다.왜 그런지 이해하는 것을 도와줄 수 있는 사람?

이제 두 번째 질문을 다루겠습니다. "왜 우리 반 대부분의 학생들은 표시된 코드가 정의되지 않은 행동에 해당한다고 말합니까?"라는 질문인데, 지금까지 다른 포스터들이 대답하지 않은 것 같습니다.학생들의 한 부분은 다음과 같은 표현의 정의되지 않은 가치의 예를 기억할 것입니다.

f(++i,i)

당신이 주는 코드는 이 패턴에 맞지만 printf가 마지막 매개변수를 무시하기 때문에 학생들은 동작이 정의된 것으로 잘못 생각합니다.이 뉘앙스는 많은 학생들을 혼란스럽게 합니다.학생의 또 다른 부분은 데이비드 손리만큼 표준에 정통하고 위에서 설명한 정확한 이유로 "정의되지 않은 행동"이라고 말할 것입니다.

정의되지 않은 행동에 대한 지적은 맞지만, 한 가지 추가 주름이 있습니다: printf가 실패할 수 있습니다.파일 IO를 수행하는 것입니다. 실패할 수 있는 이유는 여러 가지가 있으며, 전체 프로그램과 실행되는 상황을 알지 못하면 제거할 수 없습니다.

에코 코다딕트 정답은 2입니다.

printf는 인수 2와 함께 호출되고 인쇄됩니다.

이 코드가 다음과 같은 상황에 놓이면 다음과 같습니다.

void do_something()
{
    int x=1;
    printf("%d",++x,x+1);
}

그러면 해당 함수의 동작이 완전하고 명확하게 정의됩니다.저는 물론 이것이 좋거나 정확하거나 x의 값이 나중에 결정 가능하다고 주장하는 것은 아닙니다.

출력은 항상 (가장 중요한 표준 준수 컴파일러 및 시스템의 99.98%의 경우) 2.

표준에 따르면, 이것은 정의상 "정의되지 않은 행동"인 것 같습니다. 이는 자기 합리화이며 실제로 일어날 수 있는 일, 특히 그 이유에 대해 아무 것도 말하지 않는 정의/답변입니다.

유틸리티 부목(표준 준수 검사 도구가 아님)과 부목의 프로그래머는 이를 "지정되지 않은 동작"으로 간주합니다.이것은, 기본적으로, 다음과 같은 평가를 의미합니다.(x+1)업데이트 시기에 따라 1+1 또는 2+1을 줄 수 있습니다.x사실은 끝이 났습니다.식이 폐기되므로(printf 형식은 1 인수를 읽음), 출력에는 영향이 없으며, 그래도 2라고 할 수 있습니다.

undefined.c:7:20: 인수 2가 인수 3에서 사용하는 x를 수정합니다. (실제 매개 변수의 평가 순서는 정의되지 않음): printf("%d\n"), ++x, x +1) 코드에 지정되지 않은 동작이 있습니다.함수 매개 변수 또는 하위 표현식의 평가 순서는 정의되지 않으므로 순서 제한 평가 순서로 구분되지 않고 다른 위치에서 값을 사용하고 수정하면 표현식의 결과가 지정되지 않습니다.

앞서 말한 바와 같이, 특정되지 않은 행동은 단지 다음과 같은 평가에 영향을 미칩니다.(x+1), 그것의 전체적인 진술이나 다른 표현은 아닙니다.따라서 "특정되지 않은 행동"의 경우 출력은 2라고 말할 수 있으며 아무도 이의를 제기할 수 없습니다.

하지만 이것은 특정되지 않은 행동이 아니라 "정의되지 않은 행동"인 것 같습니다.그리고 "정의되지 않은 행동"은 하나의 표현 대신에 전체 진술에 영향을 미치는 것으로 보입니다.이는 "정의되지 않은 행동"이 실제로 발생하는 곳(즉, 정확하게 영향을 미치는 것)에 대한 미스터리 때문입니다.

"정의되지 않은 행동"을 단지 "정의되지 않은 행동"에 붙일 동기가 있다면,(x+1)식은 "지정되지 않은 동작"의 경우와 마찬가지로 출력은 항상 (100%) 2라고 말할 수 있습니다."정의되지 않은 동작"을 단지 에 첨부하는 것(x+1)1+1인지 2+1인지 말할 수 없음을 의미하며, 단지 "anything"일 뿐입니다.그러나 다시 말하지만, 인쇄물 때문에 "무엇이든"이 떨어지며, 이는 답이 "항상(100%) 2"가 된다는 것을 의미합니다.

대신에, 신비로운 비대칭 때문에, "정의되지 않은 행동"은 단지 그것에 부착될 수 없습니다.x+1, 하지만 실제로 그것은 적어도 그것에 영향을 줄 입니다.++x(그런데 정의되지 않은 행동에 대한 책임이 있는 것), 전체 진술은 아니더라도 말입니다.만약 감염이 된다면,++x은 "되지 않은 식, 값",수예: -5847834는 9032)다를은합니다.만약 그것이 전체 문장을 감염시킨다면, 당신은 콘솔 출력에서 가가비를 볼 수 있을 것이고, 아마도 당신의 cpu가 질식하기 시작하기 전에 ctrl-c로 프로그램을 중지해야 할 가능성이 있습니다.

한 도시의 전설에 따르면, "정의되지 않은 행동"은 전체 프로그램뿐만 아니라 여러분의 컴퓨터와 물리 법칙까지 감염시켜 신비한 생명체들이 여러분의 프로그램에 의해 창조되어 여러분을 날아가거나 잡아먹을 수 있다고 합니다.

어떤 대답도 주제에 대해 능숙하게 설명하지 못합니다.그들은 단지 "오, 표준이 이렇게 말하는 것을 보라"는 것입니다 (그리고 그것은 그저 해석일 뿐입니다, 평소처럼!).그래서 최소한 여러분은 "표준이 존재한다"는 것을 배웠을 것이고, 그 표준들은 교육적인 질문들을 건조하게 만듭니다. (물론, 여러분의 코드가 틀렸다는 것을 잊지 마세요, 명확하지 않은 행동주의와 다른 표준적인 사실들에 관계없이), 논리적인 논쟁들은 쓸모가 없고, 깊은 조사와 이해를 목적 없이 합니다.

언급URL : https://stackoverflow.com/questions/3450582/is-this-undefined-c-behaviour

반응형