상수 식에서 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/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 곱셈 연산자
...
- / 연산자의 결과는 첫 번째 피연산자를 두 번째 피연산자로 나눈 몫이고 % 연산자의 결과는 나머지입니다.두 작업 모두에서 두 번째 피연산자의 값이 0이면 동작이 정의되지 않습니다.
그리고 3장의 정의되지 않은 행동에 대해서도.용어, 정의 및 기호:
3.4.3
- 되지 않은 은 다음과 같습니다.
사항을 데이터의 의 동작 국 제 표 이 준 요 하 거 램 생 프 또 경 경 사 데 우 의 우 한 를 용 터 이 잘 된 못 는 자 본 나 잘 그 불 된 로 능 못 이 가 구 사 항 을 부 않 식 는 지 하 과 , ▁behavior ▁use ▁data ▁construct , ▁upon 경 ▁program- 참고 예측할 수 없는 결과로 상황을 완전히 무시하는 것에서부터 번역 또는 프로그램 실행 중에 환경 특성에 따라 문서화된 방식으로 행동하는 것(진단 메시지 발행 여부에 관계없이),(진단 메시지 발급과 함께) 번역 또는 실행을 종료합니다.
따라서 컴파일러를 크래시할 수 있습니다.
다른 사람들은 이미 기준에서 관련 텍스트를 언급했기 때문에, 저는 그것을 반복하지 않을 것입니다.
내 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
'codememo' 카테고리의 다른 글
| "수입*"이 나쁜 이유는 무엇입니까? (0) | 2023.06.17 |
|---|---|
| 부울 배열을 int 배열로 변환하는 방법 (0) | 2023.06.17 |
| iOS의 Firebase Analytics 이벤트가 표시되지 않음 (0) | 2023.06.17 |
| 인덱스 및 개체 유형이 아닌 Pandas DataFrame에서 값을 가져오는 방법 (0) | 2023.06.12 |
| VueJS + VUEX - 데이터 전송 관련 문제 (0) | 2023.06.12 |