사실상 무한한 것처럼 보이는 'for'루프


82

현재 일부 코드를 디버깅하고 있는데 다음 줄을 보았습니다.

for (std::size_t j = M; j <= M; --j)

(휴가중인 사장님이 작성했습니다.)

정말 이상해 보입니다.

그것은 무엇을합니까? 나에게는 무한 루프처럼 보입니다.


31
size_t부호가 없으므로 0을 사용하지 않고 루프를 종료하려고 할 때 최대 값으로 래핑됩니다. 그래도 여전히 끔찍한 코드.
BoBTFish

4
@Bathsheba에 M대한 최대 값이 발생하면 size_t어떨까요? 아직도 영리하다고 생각하십니까?
토르스텐 Dittmar

8
@Barmar Does n't come up is not never comes up , 그래서 확률은> 0입니다. 이것은 간단하게 표현할 방법이있을 때이 솔루션이 어리 석다는 것을 의미합니다.
토르스텐 Dittmar

38
나는 반대 투표를 이해하지 못합니다. 이 질문은 분명합니다. 어떻게 개선 할 수 있을지 모르겠습니다. 새로운 사용자로부터이 질문은 실제로 흥미 롭습니다 !
밧세바

17
#define TRUE FALSE그리고 휴가를 가십시오.
Leo Heinsaar

답변:


68

std::size_tC ++ 표준에 의해 unsigned유형 이 보장됩니다 . 그리고 unsigned유형을 0에서 감소 시키면 표준은 그 결과가 해당 유형에 대해 가장 큰 값임을 보장합니다.

그 래핑 된 값은 항상 M1 보다 크거나 같으 므로 루프가 종료됩니다.

따라서 유형에 j <= M적용될 때 unsigned"루프를 0으로 실행 한 다음 중지"라고 말하는 편리한 방법입니다.

j원하는 것보다 더 크게 실행 하거나 슬라이드 연산자를 사용하는 것과 같은 대안이 for (std::size_t j = M + 1; j --> 0; ){존재합니다. 더 많은 입력이 필요하지만 분명히 더 명확합니다. 그러나 한 가지 단점은 (첫 번째 검사에서 생성되는 당황스러운 효과를 제외하고) Java와 같이 서명되지 않은 유형이없는 언어로 잘 이식되지 않는다는 것입니다.

또한 상사가 선택한 계획은 unsigned세트 에서 가능한 값을 "차용" 합니다.이 경우에 M설정된 경우 std::numeric_limits<std::size_t>::max()올바른 동작을 갖지 않습니다. 사실,이 경우 루프는 무한 합니다. (그것이 당신이 관찰하고있는 것입니까?) 코드에 그 효과에 대한 주석을 삽입해야하고, 심지어 그 특정 조건에 대해 주장해야합니다.


(1) 에 따라 M되지 않는 std::numeric_limits<std::size_t>::max().


7
나는 날씨를 비난하고있다.
Hatted 수탉

3
M 인 경우 std::numeric_limits<std::size_t>::max()다음 M + 1제로, 그리고 것 for (std::size_t j = M + 1; j --> 0; )루프는 전혀 반복되지 않습니다.
Pete Kirkham

51
"슬라이드 연산자"라고 부르지 마십시오. C ++에는 그러한 연산자가 없으며 영리 해 보이는 난독 화일뿐입니다.
You

26
@DimitarMirchev : 운영자가 아니기 때문입니다. 간격이 이상한 두 연산자입니다. 인터뷰 대상자들에게 관련없는 질문을 고집한다면이를 관용구라고 부르십시오 (그러나 이상적으로는 "영리한"구문에 대해 묻는 대신 기능을 구현하는 방법을 물어보십시오).
당신

1
size_t64 비트 라고 가정하면 엣지 케이스에서 잘못된 동작을 관찰하는 데 수백 년이 걸립니다. (옵티마이 저가 루프를 제거 할 수있는 경우를 제외하고.)
Carsten S

27

상사가 하려고 했던 것은 M0부터 0까지 카운트 다운 하여 각 숫자에 대해 몇 가지 작업을 수행하는 것이 었습니다 .

불행히도 그것이 실제로 무한 루프를 제공 할 수있는 M최대 size_t값 인 경우 가 있습니다. 그리고 서명되지 않은 값이 0에서 감소 할 때 무엇을할지는 잘 정의되어 있지만, 저는 코드 자체가 엉성한 사고의 예라고 주장합니다. 특히 상사 시도의 결점없이 완벽하게 실행 가능한 솔루션이 있기 때문입니다.

더 안전한 변형 (내 의견으로는 여전히 엄격한 범위 제한을 유지하면서 더 읽기 쉬움)은 다음과 같습니다.

{
    std::size_t j = M;
    do {
        doSomethingWith(j);
    } while (j-- != 0);
}

예를 들어 다음 코드를 참조하십시오.

#include <iostream>
#include <cstdint>
#include <climits>
int main (void) {
    uint32_t quant = 0;
    unsigned short us = USHRT_MAX;
    std::cout << "Starting at " << us;
    do {
        quant++;
    } while (us-- != 0);
    std::cout << ", we would loop " << quant << " times.\n";
    return 0;
}

이것은 기본적으로와 동일한 작업을 수행 unsigned short하며 모든 단일 값을 처리하는 것을 볼 수 있습니다 .

Starting at 65535, we would loop 65536 times.

do..while위 코드 의 루프를 상사가 기본적으로 수행 한 작업으로 대체하면 무한 루프가 발생합니다. 그것을 시도하고보십시오 :

for (unsigned int us2 = us; us2 <= us; --us2) {
    quant++;
}

7
이제 엣지 케이스는 0. 왜 안돼 for(size_t j = M; j-- != 0; )?
LogicStuff

예, 코너 케이스가 numeric_limits<size_t>::max().
Bathsheba

7
@Logic 외, 내가 제공 한 방법에는 가장자리 케이스가 없으며이를 보여주는 코드를 추가했습니다. 귀하와 제안 된 솔루션의 초기 값은 M == 0그렇게 처리되는 원소 0이 될 것이다 않는 에지 경우가있다. 내 사후 확인 do..while방법을 사용 하면 가장자리 케이스가 완전히 제거됩니다. 를 사용하여 시도하면 M == 11 0 이 모두 수행되는 것을 볼 수 있습니다. 유사하게 max_size_t(어떤 일이 발생하든) 시작하여 해당 지점에서 성공적으로 시작한 다음 0까지 감소하여 0을 포함합니다.
paxdiablo

흥미롭게도이 사례는“ exitif”, 즉 { j =M; for(;;){ f(j); if( j == 0 )break; j -= 1; } }.를 사용하기 때문에“구조화되지 않은 코드”라고 말하는 것을 사용하도록 동기를 부여합니다 . 하나는 심지어 교체 할 필요가 없습니다 수 breakgotoC가 더 루프라는 이름의 이후 상황이 중첩받을 경우. "구조화 됨"이 "청크에 대한 추론에 민감 함"을 의미하는 경우 구조화되어 있으며 (레이아웃이 도움이됩니다!) 또한 "사전 및 사후 조건에 대한 추론을 통해 공식적인 검증에 민감 함"을 의미합니다. 이 경우에도 j--작동 하지만 더 복잡한 전환이 필요할 때 exitif 스타일이 정당화 될 수 있습니다.
PJTraill

@PJTraill 나는 당신이 말하는 것이 구조화되어 있고 당신이 말하는 것이 구조화되어 있지 않다는 것을 말할 수 없습니다. do-while에 대한 추론은 간단합니다. 더 이상 출구를 "사용"하지 않으며 구조화되지 않은 방식으로 더 많이 사용하지 않습니다.
philipxy
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.