source

반면 (1); C에 정의되지 않은 동작이 있습니까?

manycodes 2023. 10. 28. 08:03
반응형

반면 (1); C에 정의되지 않은 동작이 있습니까?

C++11에서는 Undefined Behavior 이지만 C에서는while(1);정의되지 않은 동작입니까?

그것은 명확한 행동입니다.C11에서 새로운 조항 6.8.5 ad 6이 추가되었습니다.

제어 표현식이 상수 표현식이 아니며,156) 입출력 동작을 수행하지 않으며, 휘발성 객체에 접근하지 않으며, 본체 내에서 동기화 또는 원자 동작을 수행하지 않으며, 표현식을 제어하거나, (a for문의 경우) 표현식-3,종료할 이행 상황을 가정할 수 있습니다.157)


157)이것은 종료를 증명할 수 없는 경우에도 빈 루프를 제거하는 것과 같은 컴파일러 변환을 허용하기 위한 것입니다.

루프의 제어 표현은 상수이므로 컴파일러는 루프가 종료된다고 가정하지 않을 수 있습니다.이는 운영 체제와 같이 영원히 실행되어야 하는 반응형 프로그램을 위한 것입니다.

그러나 다음 루프의 경우 동작이 명확하지 않습니다.

a = 1; while(a);

실제로 컴파일러는 이 루프를 제거할 수도 있고 제거하지 않을 수도 있으므로 프로그램이 종료되거나 종료되지 않을 수도 있습니다.하드 디스크를 지우는 것은 허용되지 않기 때문에 실제로 정의되지 않은 것은 아니지만 피해야 할 구성입니다.

그러나 또 다른 문제가 있습니다. 다음 코드를 고려해 보십시오.

a = 1; while(a) while(1);

이제 컴파일러는 외부 루프가 종료된다고 가정할 수 있으므로 내부 루프도 종료되어야 합니다. 그렇지 않으면 외부 루프가 종료될 수 있습니다.그래서 만약 당신이 정말 똑똑한 컴파일러를 가지고 있다면 a.while(1);종료되지 않아야 하는 루프는 끝까지 그 주위에 그러한 비종단 루프를 가지고 있어야 합니다.main. 만약 당신이 정말로 무한 루프를 원한다면, 당신은 읽거나 쓰는 것이 좋을 것입니다.volatile가변적인.

이 조항이 실용적이지 않은 이유

우리 컴파일러 회사가 이 조항을 사용할 가능성은 매우 희박한데, 이 조항은 주로 매우 구문적인 속성이기 때문입니다.중간 표현(IR)에서, 상기 예시들에서의 상수와 변수의 차이는 상수 전파를 통해 쉽게 손실됩니다.

이 조항의 의도는 컴파일러 작성자들이 다음과 같은 바람직한 변환을 적용할 수 있도록 하는 것입니다.매우 드물지 않은 루프를 생각해 보십시오.

int f(unsigned int n, int *a)
{       unsigned int i;
        int s;
        
        s = 0;
        for (i = 10U; i <= n; i++)
        {
                s += a[i];
        }
        return s;
}

아키텍처상의 이유(예: 하드웨어 루프)로 인해 이 코드를 다음과 같이 변환하고자 합니다.

int f(unsigned int n, int *a)
{       unsigned int i;
        int s;
        
        s = 0;
        for (i = 0; i < n-9; i++)
        {
                s += a[i+10];
        }
        return s;
}

조항 6.8.5 ad 6이 없으면 이것은 불가능합니다.n대등한UINT_MAX, 루프가 종료되지 않을 수 있습니다.그럼에도 불구하고 이것이 이 코드의 작성자의 의도가 아니라는 것은 인간에게 꽤 분명합니다.조항 6.8.5 광고 6은 이제 이러한 변환을 허용합니다.그러나 이를 달성하는 방법은 IR에서 무한 루프의 구문적 요구사항을 유지하기가 어렵기 때문에 컴파일러 작성자에게는 그다지 실용적이지 않습니다.

다음과 같은 것이 필수적입니다.n그리고.i이다unsigned에 넘쳐나는 대로signed int정의되지 않은 동작을 제공하므로 이러한 이유로 변환이 정당화될 수 있습니다.효율적인 코드 그러나 사용으로 인한 이점unsigned, 더 큰 양의 범위는 차치하고.

대안적 접근법

우리의 접근 방식은 코드 작성자가 예를 들어 삽입함으로써 그의 의도를 표현해야 하는 것입니다.assert(n < UINT_MAX)루프나 프라마-C 같은 보장을 받기 전에 말입니다.이렇게 하면 컴파일러는 종료를 "증명"할 수 있고 조항 6.8.5 ad 6에 의존할 필요가 없습니다.

추신: paxdiablo가 분명히 다른 버전을 보고 있기 때문에 2011년 4월 12일 초안을 보고 있습니다. 어쩌면 그의 버전이 더 새로운 것일지도 모릅니다.그의 인용문에는 일정한 표현의 요소가 언급되어 있지 않습니다.

C99 표준 초안을 확인한 결과 "아니오"라고 할 수 있습니다. 정의되지 않은 것은 아닙니다.초안에서 반복이 종료되어야 하는 요구사항을 언급한 언어를 찾을 수 없습니다.

반복문의 의미론을 설명하는 문단의 전문은 다음과 같습니다.

반복문은 제어식이 0과 동일하게 비교될 때까지 반복적으로 루프 본체라는 문을 실행하게 합니다.

해당되는 경우 C++11에 지정된 것과 같은 제한이 거기에 나타나기를 기대합니다.또한 "제약"이라는 섹션이 있는데, 이 섹션 역시 그러한 제약에 대해 언급하지 않습니다.

물론, 제가 의심하기는 하지만 실제 기준은 다른 말을 할 수도 있습니다.

가장 간단한 답변은 §5.1.2.3p6의 인용문으로, 적합한 구현의 최소 요구사항을 다음과 같이 설명합니다.

적합한 구현에 대한 최소한의 요구사항은 다음과 같습니다.

— 휘발성 개체에 대한 액세스는 추상 시스템의 규칙에 따라 엄격하게 평가됩니다.

— 프로그램 종료 시 파일에 기록되는 모든 데이터는 추상적 의미론에 따라 프로그램이 실행되었을 때 생성된 결과와 동일해야 합니다.

— 상호작용 장치의 입력 및 출력 역학은 7.21.3에 명시된 대로 이루어져야 합니다.이러한 요구 사항의 목적은 입력을 기다리는 프로그램 전에 실제로 프롬프트 메시지가 나타나도록 버퍼링되지 않거나 라인 버퍼링된 출력이 가능한 한 빨리 표시되도록 하는 것입니다.

이것이 프로그램의 관찰 가능한 행동입니다.

기계 코드가 수행된 최적화로 인해 관찰 가능한 동작을 생성하지 못하는 경우 컴파일러는 C 컴파일러가 아닙니다.이러한 무한 루프만을 포함하는 프로그램의 종료 시점에서 관찰 가능한 동작은 무엇입니까?그러한 루프가 종료될 수 있는 유일한 방법은 신호가 조기에 종료되도록 하는 것입니다.의 경우SIGTERM, 프로그램이 종료됩니다.이로 인해 관찰 가능한 동작이 발생하지 않습니다.따라서 해당 프로그램의 유일하게 유효한 최적화는 컴파일러가 시스템을 미리 비우고 프로그램을 즉시 종료하는 프로그램을 생성하는 것입니다.

/* unoptimised version */
int main() {
    for (;;);
    puts("The loop has ended");
}

/* optimised version */
int main() { }

한 가지 가능성은 신호가 상승하고 longjmp가 호출되어 실행이 다른 위치로 점프할 수 있습니다.점프할 수 있는 유일한 장소는 루프 이전에 실행 중에 도달한 곳인 것 같습니다. 따라서 컴파일러가 신호가 상승하여 실행이 다른 곳으로 점프한다는 것을 알아차릴 수 있을 정도로 지능적이라면 루프(및 신호 상승)를 즉시 점프하기 위해 최적화할 수 있습니다.

여러 스레드가 수식에 들어가면 유효한 구현이 프로그램의 소유권을 메인 스레드에서 다른 스레드로 이전하고 메인 스레드를 종료할 수 있습니다.최적화에 관계없이 프로그램의 관찰 가능한 동작은 여전히 관찰 가능해야 합니다.

다음 문장이 에 나타납니다.C11 6.8.5 Iteration statements /6:

제어 표현식이 상수 표현식이 아니며, 입출력 동작을 수행하지 않으며, 휘발성 객체에 접근하지 않으며, 본체 내에서 동기화 또는 원자 동작을 수행하지 않으며, 표현식을 제어하거나, (a for문의 경우) 표현식-3,종료할 이행 상황을 가정할 수 있습니다.

부터.while(1); 는 일정한 식을 사용하며, 구현은 종료될 것이라고 가정할 수 없습니다.

컴파일러는 그러한 루프를 완전히 제거할 수 있는 자유는 표현식이 일정하지 않고 다른 모든 조건이 유사하게 충족되는 경우이며, 이는 루프가 종료된다는 것을 결정적으로 증명할 수 없더라도 말입니다.

언급URL : https://stackoverflow.com/questions/16436237/is-while1-undefined-behavior-in-c

반응형