C에서 + (+ k--) 표현식


9

다음 코드의 출력을 알려주는 테스트 에서이 질문을 보았습니다.

#include<stdio.h>

int main(){
    int k = 0;
    while(+(+k--)!=0)
    k=k++;
    printf("%d\n", k);  
    return 0;
}

출력은 -1입니다. 그래도 이것이 왜 대답인지 잘 모르겠습니다.

+(+k--)C에서 그 표현 은 무엇을 의미합니까?


4
이 형식으로되어 있습니까? 그 의미는 ;-)
피터-복원 모니카

3
힌트 : 실제와 같은 BS 코드를 작성하지 마십시오.
Jabberwocky

5
이것은 UB 가 아닙니다 . 내 대답을 참조하십시오.
dbush

@ Peter-ReinstateMonica 예, 다음과 같이 포맷되었습니다.
Ankur Gautam

2
때문에 인위적인 자연과 기괴한 왜곡의 - k=k++정의되지 않은,하지만이 때문에 난독 상태로 실행되지 않습니다 있기 때문에 그것은 정의되지 아니에요 - 난의 중복으로 닫 투표를하고있어 이 질문에 .
Steve Summit

답변:


10

[기록을 위해이 답변을 수락하고 투표 한 이후로이 답변을 상당히 많이 편집했습니다. 그래도 여전히 기본적으로 같은 것을 말합니다.]

이 코드는 고의적으로 혼동 될 수 있습니다. 그것은 겁에 질리지 않은 dread undefined behavior의 인스턴스를 포함합니다 . 이 질문을 만든 사람이 매우, 매우 영리한지, 매우 멍청한 지 판단하는 것은 기본적으로 불가능합니다. 그리고이 코드가 당신에게 가르치거나 퀴즈를 내릴 수있는 "믿지 않는"즉, 단항 더하기 연산자가 많은 것을하지 않는다는 것은 확실히 이런 종류의 파괴적인 오해를받을만한 가치가있는 것은 아닙니다.

코드의 두 가지 혼란스러운 측면, 이상한 조건이 있습니다.

while(+(+k--)!=0)

그리고 그것이 통제하는 진술 문 :

k=k++;

두 번째 부분을 먼저 다루겠습니다.

k1만큼 증가시키고 싶은 변수가 있다면 C는 두 가지가 아니라 세 가지가 아니라 네 가지 다른 방법을 제공합니다.

  1. k = k + 1
  2. k += 1
  3. ++k
  4. k++

이 현상금에도 불구하고 (또는 아마도 그로 인해) 일부 프로그래머는 혼란스럽고 뒤틀리는 기침을 유발합니다.

k = k++;

이 작업을 수행 할 수없는 경우 아무도 걱정할 필요가 없습니다. 이 표현에는 k값 ( k =부분과 k++부분) 을 변경하려는 두 가지 다른 시도가 포함되어 있으며 , C에는 어떤 수정 시도가 "승리"할 것인지에 대한 규칙이 없기 때문에 이와 같은 표현은 공식적으로 정의되지 않습니다. 그것은 한 어떤 의미를 정의하지 않지만, 전체 프로그램은 용의자 포함하는 것이다.

당신이 보면 지금 매우 신중하게,이 특정 프로그램에서 선 것을 볼 수 있습니다 k = k++(우리가 볼 수 있도록 대한 것 같은) 제어 조건이 처음에 거짓이기 때문에 루프가 0 번 실행되도록 실제로 실행되지 않습니다 . 따라서이 특정 프로그램은 실제로 정의되지 않았을 수도 있지만 여전히 병리 적으로 혼란 스럽습니다.

이러한 종류의 정의되지 않은 동작에 관한 모든 질문에 대한 정식 SO 답변 도 참조하십시오 .

그러나 당신은 그 k=k++부분 에 대해 묻지 않았습니다 . 첫 번째 혼란스러운 부분 인 +(+k--)!=0상태 에 대해 물었습니다 . 이 때문에, 이상한 모양 입니다 이상한. 실제 프로그램에서 그러한 코드를 작성하는 사람은 없습니다. 따라서 그것을 이해하는 방법을 배울 이유가 없습니다. (예, 사실, 시스템의 경계를 탐색하는 것이 좋은 점에 대해 배우는 데 도움이 될 수 있지만, 내 책에는 상상력이 있고 생각을 자극하는 탐험과 험한 탐험, 험난한 탐험 사이에 분명한 선이 있습니다.이 표현은 매우 명확합니다. 그 선의 잘못된면.)

어쨌든 살펴 보자 +(+k--)!=0. (그리고 그렇게 한 후에는 그것에 대해 모든 것을 잊어 봅시다.) 이와 같은 표현은 내부에서 밖으로 이해되어야합니다. 나는 당신이 무엇을 알고 있다고 가정

k--

그렇습니다. k현재 값을 가져 와서 나머지 표현식으로 "반환"하며, 동시에 거의 감소합니다.k . 즉, 수량을에 k-1다시 저장합니다 k.

그러면 어떻게 +해야합니까? 이것은 이진 플러스가 아니라 단항 플러스입니다. 단항 마이너스와 같습니다. 이진 빼기에서 빼기를 수행한다는 것을 알고 있습니다.

a - b

a에서 b를 뺍니다. 그리고 당신은 단항 마이너스가 물건을 부정한다는 것을 알고 있습니다 : 표현

-a

당신에게 a의 부정을 제공합니다. 단항식 +은 ... 기본적으로 아무것도 아닙니다. 양수 값을 양수로 변경하고 음수 값을 음수로 변경 한 후의 값을 +a제공합니다 a. 그래서 표현

+k--

무엇이든 준다 k-- 에게 준 것, 즉 k오래된 가치를 제공합니다.

하지만 아직 끝나지 않았습니다.

+(+k--)

이것은 +k--당신에게 주어진 +것을 취하고 단항식 을 다시 적용 합니다. 그래서 그것은 당신에게 무엇을 주 +k--었는지, 그것이 무엇이든 k--당신에게 주었던 것입니다. 그것은 k오래된 가치였습니다.

결국 조건은

while(+(+k--)!=0)

훨씬 더 평범한 조건과 정확히 같은 일을합니다

while(k-- != 0)

했을 것입니다. (또한 더 복잡한 모양의 조건과 동일한 기능 while(+(+(+(+k--)))!=0)을 수행합니다. 괄호는 실제로 필요하지 않습니다. 또한 동일한 동작 while(+ + + +k--!=0)을 수행합니다.)

어떤 "정상적인"상태인지 파악

while(k-- != 0)

까다 롭습니다. 이 루프에는 두 가지 일이 있습니다. 루프가 여러 번 실행될 수 있으므로 다음과 같이하겠습니다.

  1. 계속 해 k-- 해서 k작고 작게 만들지 만
  2. 루프 바디를 계속 수행하십시오.

그러나 우리는 k--루프를 통해 또 다른 여행을 할 것인지 결정하기 직전에 (또는 과정에서) 부분을 수행합니다. 그리고 k--값을 k낮추기 전에 이전 값 을 "반환" 한다는 것을 기억 하십시오. 이 프로그램에서 초기 값 k은 0입니다. 따라서 k--이전 값 0을 "반환"한 다음 k-1 로 업데이트 합니다. 그러나 나머지 조건은 다음과 같습니다. 그러나 != 0방금 보았 듯이 조건을 처음 테스트했을 때 0을 얻었습니다. 따라서 루프를 통과하지 않으므로 루프를 실행하려고 시도하지 않습니다. 문제가있는 진술k=k++ .

다시 말해서,이 특정 루프에서, "두 가지 일이 진행되고있다"고 말했지만, 일 1은 한 번 발생하지만 일 2는 0 번 발생한다는 것이 밝혀졌습니다.

어쨌든, 나는 왜 프로그램에 대한 불쌍한 핑계가 -1을 최종 값으로 인쇄하는 이유를 충분히 분명히 알기를 바랍니다 k. 일반적으로 저는 이런 질문에 답하기를 좋아하지 않습니다 – 부정 행위처럼 느껴집니다.하지만이 경우 운동의 전체 요점에 너무나 동의하지 않기 때문에 괜찮습니다.


1
고안된 사례는 모든 기본 수업에서 일반적입니다. 시험 에서 운영자 우선 순위 에 대한 간결한 질문을 어떻게 작성 하시겠습니까? 나는 수학을 가르치고, 매번 학생이 " 실제로 언제 이것을 할 것인가?" 라고 물을 때마다 니켈이 있다면 ...
Scott

4
@Scott가 인위적인 것, 그리고 거기에 인위적인 . 운전자 훈련 강사라고 가정하십시오. 학생이 야구 방망이로 머리를 때리는 동안 시내 교통 체증을 겪게한다고 가정 해 봅시다. 아마도 학생은 잠재적으로 유용한 기술을 배우게 될 것입니다. 그러나 나는 이것을 남용이라고 부릅니다. 그리고 나는이 질문의 코드가 부정적 인 교육적 가치를 가지고 모욕적이라는 견해를 가지고 있습니다.
Steve Summit

1
"나는 내 의견으로 서있다"이것은 공평하지만 여기서 키워드는 "의견"입니다. StackOverflow 답변은 자신의 의견을 표현하기에 최적의 장소가 아닐 수도 있습니다. :)
Scott

1
기록을 위해이 답변을 수락하고 투표 한 이후로이 답변을 상당히 많이 편집했습니다. 그래도 여전히 기본적으로 같은 것을 말합니다.
Steve Summit

11

언뜻보기 에이 코드는 정의되지 않은 동작을 호출하는 것처럼 보이지만 그렇지 않습니다.

먼저 코드를 올바르게 포맷하자 :

#include<stdio.h>

int main(){
    int k = 0;
    while(+(+k--)!=0)
        k=k++;
    printf("%d\n", k);  
    return 0;
}

이제 명령문 k=k++;이 루프 내부에 있음을 알 수 있습니다 .

이제 프로그램을 추적하자 :

루프 조건을 처음 평가할 k때 값은 0입니다.이 표현식 k--현재k은 0이며 k부작용으로 감소합니다. 따라서이 명령문 이후의 값 k은 -1입니다.

+이 표현식 의 선행 은 값에 영향을 미치지 않으므로 +k--0으로 평가되고 유사하게+(+k--) 평가 0으로 평가됩니다.

그런 다음 !=연산자가 평가됩니다. 0!=0false 이므로 루프 본문이 입력되지 않습니다 . 본문을 입력 한 경우 시퀀스 포인트없이 k=k++읽고 쓸 k수 있으므로 정의되지 않은 동작을 호출하게 됩니다. 그러나 루프는 입력되지 않으므로 UB는 없습니다.

마지막으로 k-1 인 값 이 인쇄됩니다.


1
실행되지 않은 정의되지 않은 동작이 정의되어 있지 않은지 여부는 공개적이고 실존 적이며 철학적이며 논란의 여지가없는 질문입니다. 정의되지 않았습니까? 나는 그렇게 생각하지 않습니다.
Steve Summit

2
@SteveSummit 존재적이고, 철학적이며, 그것에 대해 타의 추종을 불허하는 것은 전혀 없습니다. if (x != NULL) *x = 42;언제 정의되지 x == NULL않습니까? 당연히 아니지. 실행되지 않는 코드 부분에서는 정의되지 않은 동작이 발생하지 않습니다. 단어 행동 은 힌트입니다. 실행되지 않은 코드는 동작, 정의되지 않은 방식 또는 다른 방식으로 동작하지 않습니다.
n. '대명사'm.

1
@SteveSummit 실행되지 않은 정의되지 않은 동작이 전체 프로그램을 무효화하는 경우 C 프로그램은 동작을 정의하지 않습니다. C 프로그램은 항상 UB 실행과 거리가 먼 루프 / if 조건이기 때문입니다. 예를 들어 간단한 배열 반복을 살펴보십시오. 바운드 검사가 실패 할 때까지 반복됩니다. 다음 루프 반복을 실행하는 것은 정의되지 않은 동작이지만 실행되지 않으므로 모든 것이 정상입니다.
cmaster-monica reinstate

3
나는 언어 변호사를하지 않기 때문에 그것에 대해 논쟁하지 않을 것입니다. 그러나 나는 당신이이 질문을하고 언어 변호사로 태그를 붙였다면, 당신은 활발한 토론을 할 수있을 것입니다. 와 k=k++질적으로 다릅니다 *x=42. 후자는 잘 정의되어 있습니다x 유효한 포인터 있지만 전자는 무엇이든 정의되지 않습니다. (나는 그것을 접경 주장하지 않을거야, 당신이 바로, 그러나 다시있을 수 있습니다 인정, 나는 점점 우리는 훌륭하게 trolled 봤는데 걱정.)
스티브 정상 회의

1
"x가 유효한 포인터이면 후자가 잘 정의되어 있지만 전자는 무엇이든 정의되지 않습니다." 전체 프로그램에만 정의되지 않은 동작이있을 수 있습니다. 이 개념은 격리 된 코드 조각에는 적용되지 않습니다.
n. '대명사'm.

3

다음은 연산자 우선 순위를 보여주는 버전입니다.

+(+(k--))

단항 +연산자 두 개 는 아무것도하지 않으므로이 식은와 정확히 같습니다 k--. 이 글을 쓴 사람이 당신의 마음을 망치려고했을 것입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.