codememo

상수 식에서 0으로 나누기

tipmemo 2023. 6. 17. 09:23
반응형

상수 식에서 0으로 나누기

상수 식에서 0으로 나누면 장난감 컴파일러가 충돌합니다.

int x = 1 / 0;

이 동작이 C 및/또는 C++ 표준에 의해 허용됩니까?

예, 0으로 나누는 것은 정의되지 않은 동작이며 C 또는 C++ 표준은 이러한 경우에 어떠한 요구 사항도 부과하지 않습니다.이 경우에는 진단서라도 발급해 주셔야 한다고 생각합니다(아래 참조)

표준을 인용하기 전에, 저는 이것이 구현의 준수 행동 품질이 다른 문제일 수 있지만 단순히 준수하는 것이 유용한 것과 같지는 않다는 것에 주목해야 합니다.gcc, clang, Visual Studio 및 Intel(tpg2114에 따름) 팀은 내부 컴파일러 오류(ICE)를 보고해야 하는 버그로 간주합니다.현재 gcc와 clang 모두 제공된 플래그와 상관없이 이 경우에 대한 경고를 생성한다는 점에 유의해야 합니다.두 피연산자가 모두 리터럴/상수인 경우, 여기서는 이를 감지하고 진단을 제공하는 것이 훨씬 간단합니다. clang은 이 경우에 대해 다음 진단을 생성합니다(라이브 참조).

warning: division by zero is undefined [-Wdivision-by-zero]
int x = 1 / 0 ;
          ^ ~

섹션 C11에서.6.5.5승법 연산자(광산 강조):

/operator의 결과는 첫 번째 피연산자를 두 번째 피연산자로 나눈 몫입니다. [...] 두 번째 피연산자의 값이 0이면 동작이 정의되지 않습니다.

정의되지 않은 행동입니다.

C 섹션 초C++ 표션섹준5.6 [expr.mul]은 다음과 같이 말합니다.

이진 / 연산자가 계수 [...]를 산출합니다. / 또는 %의 번째 피연산자가 0이면 동작이 정의되지 않습니다. [...]

다시 정의되지 않은 동작입니다.

초안 C++ 표준과 초안 C 표준 모두 정의되지 않은 동작에 대해 유사한 정의를 가지고 있습니다.

[...]이 국제표준이 요구하지 않는 경우.

문구는 어떤 요구 사항도 부과하지 않는 것처럼 비강 악마를 포함한 어떤 행동도 허용하지 않는 것처럼 보입니다.두 가지 모두 다음과 같은 유사한 내용을 담고 있습니다.

이 국제 표준에서 동작에 대한 명시적인 정의를 생략하거나 프로그램이 잘못된 생성자 또는 잘못된 데이터를 사용하는 경우 정의되지 않은 동작이 예상될 수 있습니다.허용되는 정의되지 않은 행동은 예측할 수 없는 결과로 상황을 완전히 무시하는 에서부터 번역 또는 프로그램 실행 중 환경의 특성에 따라 문서화된 방식으로 행동하는 것(진단 메시지 발행 여부에 관계없이)까지 다양합니다.(진단 메시지 발급과 함께) 번역 또는 실행을 종료합니다.

따라서 노트가 정상적이지는 않지만, 번역 중에 종료할 경우 진단서라도 발급해야 할 것 같습니다.종료가 정의되지 않았기 때문에 이것이 무엇을 허용하는지 주장하기 어렵습니다.나는 clang과 gcc가 진단 없이 ICE가 있는 경우를 본 적이 없다고 생각합니다.

코드를 실행해야 합니까?

실행되지 않을 코드를 읽을 경우 정의되지 않은 동작을 호출할 수 있습니까?우리는 적어도 C의 경우에는 논쟁의 여지가 있는 것을 볼 수 있습니다.1 / 0정의되지 않은 동작을 호출하려면 를 실행해야 합니다.더 나쁜 것은 C++ 사례에서 행동의 정의가 존재하지 않기 때문에 C 사례에 사용된 분석의 일부를 C++ 사례에 사용할 수 없다는 것입니다.

컴파일러가 코드가 절대 실행되지 않을 것이라는 것을 증명할 수 있다면 프로그램에 정의되지 않은 동작이 없다면 그렇게 것이라고 추론할 수 있는 것처럼 보이지만, 저는 이것이 증명 가능하지 않다고 생각합니다. 단지 합리적인 동작일 뿐입니다.

C의 관점에서 WG14 결함 보고서 109는 이를 더욱 명확히 합니다.다음 코드 예제가 제공됩니다.

int foo()
{
  int i;
  i = (p1 > p2); /* Must this be "successfully translated"? */
  1/0; /* Must this be "successfully translated"? */
  return 0;
} 

응답 내용은 다음과 같습니다.

게다가, 주어진 프로그램의 가능한 모든 실행이 정의되지 않은 동작을 초래한다면, 그 프로그램은 엄격하게 준수하지 않습니다.
적합한 구현은 해당 프로그램의 실행 가능성으로 인해 정의되지 않은 동작이 발생할 수 있다는 이유만으로 엄격하게 적합한 프로그램을 변환하는 데 실패해서는 안 됩니다.foo는 호출되지 않을 수 있으므로 지정된 예제는 적합한 구현을 통해 성공적으로 변환되어야 합니다.

따라서 C의 경우, 정의되지 않은 동작을 호출하는 코드가 실행될 것이라는 보장이 없는 한 컴파일러는 프로그램을 성공적으로 번역해야 합니다.

C++ constexpr 대소문자

한다면x였습니다. constexpr 변수입니다.

constexpr int x = 1 / 0 ;

잘못된 형식이며 gcc는 경고를 생성하고 clang은 오류를 발생시킵니다(라이브 보기).

error: constexpr variable 'x' must be initialized by a constant expression
constexpr int x = 1/ 0 ;
             ^   ~~~~
note: division by zero
constexpr int x = 1/ 0 ;
                  ^
warning: division by zero is undefined [-Wdivision-by-zero]
constexpr int x = 1/ 0 ;
                  ^ ~

0에 의한 나눗셈은 정의되지 않았습니다.

C 섹션 초C++ 표션섹준5.19상수 표현식 [expr.const]는 다음과 같습니다.

추상 기계(1.9)의 규칙에 따라 e의 평가가 다음 식을 평가하지 않는 한 조건부 표현식은 핵심 상수 표현식입니다.

다음 글머리 기호를 포함합니다.

정의되지 않은 동작을 갖는 연산 [주: 부호 있는 정수 오버플로(5항), 특정 포인터 산술(5.7), 0으로 나눗셈(5.6) 또는 특정 시프트 연산(5.8)—종료 노트];

C11에서 1 / 0은 상수 식입니까?

1 / 0, 에서 확인할 수 . 섹션에서 이를 확인할 수 있습니다.6.6다음과 같은 상수 표현식:

각 상수 표현식은 해당 유형에 대해 표현 가능한 값 범위에 있는 상수로 평가해야 합니다.

하지만 다음을 허용합니다.

구현은 다른 형태의 상수 식을 허용할 수 있습니다.

그렇게1 / 0는 C 또는 C++에서 상수 식을 사용하지 않지만 상수 식을 사용해야 하는 컨텍스트에서 사용되지 않기 때문에 답을 변경하지 않습니다.그 작전의 의도는1 / 0두 피연산자가 모두 리터럴이므로 지속적으로 접을 수 있습니다. 이는 충돌을 설명하기도 합니다.

의 만으로도.1 / 0컴파일러의 충돌을 허용하지 않습니다.기껏해야 표현식이 평가되지 않을 것이며 따라서 실행이 지정된 줄에 도달하지 않을 것이라고 가정할 수 있습니다.

표현식이 평가될 것이 보장되면 표준은 프로그램이나 컴파일러에 요구 사항을 부과하지 않습니다.그러면 컴파일러가 충돌할 수 있습니다.

1/0은 계산된 경우에만 UB입니다.

C11 표준은 다음의 명시적인 예를 제공합니다.1 / 0평가되지 않은 경우 정의된 동작:

따라서, 다음 초기화에서,

        static int i = 2 || 1 / 0;

식은 값이 1인 올바른 정수 상수 식입니다.

섹션 6.6, 각주 118.

1/0은 상수 식이 아닙니다.

C11 표준의 섹션 6.6은 제약 조건 아래에서 다음과 같이 말합니다.

  1. 상수 식은 평가되지 않은 하위 식에 포함된 경우를 제외하고 할당, 증분, 감소, 함수 호출 또는 쉼표 연산자를 포함할 수 없습니다.
  2. 각 상수 표현식은 해당 유형에 대해 표현 가능한 값 범위에 있는 상수로 평가해야 합니다.

1/0은 int로 표시되는 값의 범위에서 상수로 평가되지 않으므로 1/0은 상수 식이 아닙니다.이 규칙은 할당이 없는 규칙과 같이 상수 식으로 간주되는 항목에 대한 규칙입니다.적어도 C++의 경우, Clang은 1/0을 상수 식으로 간주하지 않는다는 것을 알 수 있습니다.

prog.cc:3:18: error: constexpr variable 'x' must be initialized by a constant expression
   constexpr int x = 1/ 0 ;
                 ^   ~~~~

평가되지 않은 1/0이 UB라는 것은 그다지 말이 되지 않을 것입니다.

(x == 0) ? x : 1 / xx가 0이고 1/x를 평가하는 것이 UB라고 해도 완벽하게 잘 정의되어 있습니다.만약 그런 경우라면(0 == 0) ? 0 : 1 / 0UB라면, 그건 말도 안 되는 소리야.

C 표준 초안(N1570)에서:

6.5.5 곱셈 연산자

...

  1. / 연산자의 결과는 첫 번째 피연산자를 두 번째 피연산자로 나눈 몫이고 % 연산자의 결과는 나머지입니다.두 작업 모두에서 두 번째 피연산자의 값이 0이면 동작이 정의되지 않습니다.

그리고 3장의 정의되지 않은 행동에 대해서도.용어, 정의 및 기호:

3.4.3

  1. 되지 않은 은 다음과 같습니다.
    사항을 데이터의 의 동작 국 제 표 이 준 요 하 거 램 생 프 또 경 경 사 데 우 의 우 한 를 용 터 이 잘 된 못 는 자 본 나 잘 그 불 된 로 능 못 이 가 구 사 항 을 부 않 식 는 지 하 과 , ▁behavior ▁use ▁data ▁construct , ▁upon 경 ▁program
  2. 참고 예측할 수 없는 결과로 상황을 완전히 무시하는 에서부터 번역 또는 프로그램 실행 환경 특성에 따라 문서화된 방식으로 행동하는 것(진단 메시지 발행 여부에 관계없이),(진단 메시지 발급과 함께) 번역 또는 실행을 종료합니다.

따라서 컴파일러를 크래시할 수 있습니다.

다른 사람들은 이미 기준에서 관련 텍스트를 언급했기 때문에, 저는 그것을 반복하지 않을 것입니다.

내 C 컴파일러의 표현식 평가 함수는 역폴란드 표기법(값(숫자와 식별자)과 연산자의 배열)으로 표현식을 가져와서 표현식이 상수로 평가되는지 여부를 나타내는 플래그와 상수일 경우 값(그렇지 않으면 0)이라는 두 가지를 반환합니다.결과가 상수이면 전체 RPN이 해당 상수로 감소합니다.1/0은 상수 정수 값으로 계산되지 않으므로 상수 식이 아닙니다.RPN은 1/0 동안 감소하지 않으며 그대로 유지됩니다.

C에서 정적 변수는 상수 값으로만 초기화할 수 있습니다.따라서 컴파일러는 정적 변수의 이니셜라이저가 상수가 아니라는 것을 알 때 오류가 발생합니다.자동 저장 변수는 상수가 아닌 식을 사용하여 초기화할 수 있습니다.이 경우 컴파일러는 1/0을 평가하기 위한 코드를 생성합니다(이 표현식에 대한 RPN은 여전히 있습니다!).이 코드가 런타임에 도달하면 언어 표준에 의해 규정된 대로 UB가 발생합니다.[x86에서 이 UB는 0 CPU 예외에 의한 나눗셈의 형태를 취하는 반면, MIPS에서 이 UB는 잘못된 몫 값을 산출합니다(CPU는 0 예외에 의한 나눗셈을 가지고 있지 않습니다).]

내 컴파일러는 ||-표현식 및 &&-표현식의 단락을 적절하게 지원합니다.그래서, 그것은 평가합니다.1 || 1/0하나로0 && 1/0논리 연산자의 오른쪽 피연산자가 상수인지 여부에 관계없이 0입니다.식 평가 함수는 평가되지 않아야 할 때 (연산자와 함께) 이러한 연산자의 오른쪽 피연산자를 제거합니다.1 || 1/0로 변환합니다.1 != 0(&&과 ||의 피연산자들이 0과의 비교를 거치도록 함), 이는 1과0 && 1/0로 변환합니다.0 != 0이 값은 0입니다.

처리해야 할 또 다른 사례는INT_MIN / -1그리고.INT_MIN % -1(더 큰 정수 유형의 경우 에 해당).계수는 부호 있는 int(2의 보완 부호 있는 정수의 경우, 즉 현대의 모든 CPU에서 가지고 있는 것)로 표현할 수 없으므로 UB도 마찬가지입니다(실행 시 x86에서 0 예외로 동일한 나눗셈을 얻습니다).저는 이 사건을 비슷하게 처리합니다.이 식은 정적 스토리지 변수를 초기화할 수 없으며 논리 &/| 연산자에서 평가되지 않으면 삭제됩니다.자동 변수를 초기화하여 런타임에 UB로 이어질 수 있습니다.

저는 또한 그러한 분열이 발생했을 때 경고를 보냅니다.

컴파일러의 동작 방식은 식의 값과 관련이 없습니다.컴파일러가 충돌하면 안 됩니다.마침표.

저는 현학적인 구현이 이러한 표현이 주어지면 실행 시간에 1/0을 실행하는 코드로 컴파일될 것이라고 생각하지만, 그것이 좋은 특징으로 보이지는 않을 것이라고 생각합니다.

그래서 남은 공간은 컴파일러가 컴파일을 거부하고 소스 코드 오류의 일부 클래스로 처리해야 한다는 것입니다.

언급URL : https://stackoverflow.com/questions/33916113/dividing-by-zero-in-a-constant-expression

반응형