IT

실행되지 않는 코드가 정의되지 않은 동작을 호출 할 수 있습니까?

lottoking 2020. 10. 10. 10:23

실행되지 않는 코드가 정의되지 않은 동작을 호출 할 수 있습니까?


정의되지 않은 동작 (이 예에서는 0으로 나누기)을 호출하는 코드는 실행되지 않습니다. 프로그램이 여전히 정의되지 않은 동작입니까?

int main(void)
{
    int i;
    if(0)
    {
        i = 1/0;
    }
    return 0;
}

여전히 정의되지 않은 행동이라고 생각되지 않습니다.

그래서, 어떤 아이디어?


C 표준이 "행동"및 "정의되지 않은 동작"이라는 용어를 정의하는 방법을 보겠습니다.

참조는 ISO C 2011 표준 N1570 초안에 대한 것입니다. 나는 세 가지 ISO C 표준 (1990, 1999, 2011)에 관련된 차이점을 알지 못합니다.

섹션 3.4 :

행동
외관 또는 행동

좋아, 그것은 약간 모호하지만 "외관"이없고 확실히 "조치"가 주장한다.

섹션 3.4.3 :


이식 할 수없는 오류가있는 프로그램 구성 되지 않은 동작 데이터를 사용할 때 정의되지 않은 동작 (이 국제 표준이 요구 사항을 부과하지 않음)

그것은 말하는 구조의 " 사용시 " 라고 말한다 . "사용할"이라는 단어는 표준에 정의되어 있습니다. 구성은 실행되지 않는 "사용"되지 않습니다.

그 정의 아래에 메모가 있습니다.

참고 가능한 정의되지 않은 동작은 예측할 수없는 결과로 상황을 완전히 무시하는 것, 번역 또는 프로그램 실행 중에 환경 특성 (진단 발행 여부에 관계없이)의 문서화 된 방식으로 동작하는 것, 번역 또는 실행 종료 (진단 메시지 발행).

컴파일러는 따라서 동작이 정의되지 않은 경우 컴파일 타임에 프로그램을 거부 할 수 있습니다 . 그것에, 대한 나의 그러나 해석은 프로그램의 모든 실행이 정의되지 않은 동작을 만날 것이라는 것을 증명할 수 있을 때만 그렇게 할 수 있다는을 구석으로입니다. 내 생각에 다음을 의미합니다.

if (rand() % 2 == 0) {
    i = i / 0;
}

확실히 정의되지 않은 동작을 가질 있으며 컴파일 타임에 거부 할 수 없습니다.

표준은 수행 할 수 있어야하며 표준은이를 호출하는 것을 방지하기 위해 작동 테스트를 수행해야합니다.

귀하의 예는 다음과 가변적입니다.

if (0) {
    i = 1/0;
}

0으로 나누기를 실행하지 않습니다. 매우 일반적인 관용구는 다음과 가변적입니다.

int x, y;
/* set values for x and y */
if (y != 0) {
    x = x / y;
}

분할은 확실히 정의되지 않은 동작을 가지고 y == 0있습니다 y == 0. 동작은 잘 정의되어 있으며, 잘 정의가 예제 된 것과 같은 이유로 잠재적 인 정의되지 않은 동작은 실제로 발생할 수 없기 때문 입니다.

( INT_MIN < -INT_MAX && x == INT_MIN && y == -1(예, 정수 나눗셈이 오버플로 될 수있는 경우 가 아니라면 ) 별도의 문제입니다.)

(삭제 이후) 주석에서 누군가 컴파일러가 있다는 점에서 상수에 평가할 수 있습니다. 사실이지만이 경우에는 관련이 없습니다.

i = 1/0;

1/0 상수가 아닙니다 .

일정 집합으로 감소 통사 범주이다 조건부 (제외되는 할당 및 콤마 식). 생산 상수 표현식 은 케이스 레이블과 같이 실제로 상수 표현식이 필요한 컨텍스트 에서만 문법에 나타납니다 . 따라서 다음과 같이 작성하면 :

switch (...) {
    case 1/0:
    ...
}

그 다음 1/0은 상수이고 6.6p4의 제약 조건을 사용하는 것입니다. "각 상수는 해당 유형에 대해 표현 가능한 값 범위에있는 상수로 평가되어야합니다.", 진단이 필요합니다. 그러나 할당의 오른쪽에는 일정한 표현이 필요하지 않고 단지 조건부 표현 이 필요하므로 상수 표현식에 대한 제약 조건이 적용되지 않습니다 . 컴파일러는 컴파일 타임에 사용할 수있는 모든 표현식을 평가할 수 있지만 , 동작이 실행 중에 평가 된 것과 동일한 경우 (의 컨텍스트 또는 에서 실행 () 평가 중에 되지 않은if (0) 경우 에만 ).

(정확히 같다고 뭔가 일 표현이 반드시 아닌 정수 표현 에서와 연속이 x + y * z시퀀스는 x + y어서 표현 때문에 표현 때문에).

이는 인용하려는 각 N1570 섹션 6.6의 각주를 의미합니다.

다음 초기화 초기화
static int i = 2 || 1 / 0;
에서 식은 값이 1 인 유효한 정수 상수 식입니다.

실제로이 질문과 관련이 없습니다.

마지막으로, 실행 중에 발생하는 일이 아닌 정의되지 않은 동작을 유발하도록 정의 된 몇 가지 사항이 있습니다. C 표준의 초기화 J, 섹션 2 (다시 N1570 초안 참조 )에는 정의되지 않은 동작을 유발하는 항목이 있고 나머지 표준에서 수집되었습니다. 몇 가지 예 (완전한 목록이라고 주장하지 않음)는 다음과 같다.

  • 비어 있지 않은 소스 파일은 백 슬래시 문자가 바로 앞에 나오지 또는 부분 전처리 토큰 또는 주석으로 끝나는 개행 문자로 끝나지 않습니다.
  • 토큰 연결은 범용 문자 이름의 구문과 일치하는 문자 시퀀스를 생성합니다.
  • 기본 소스 문자 세트에없는 문자가 소스 파일에서 발견되었습니다. 단, 식별자, 문자 상수, 문자열 리터럴, 헤더 이름, 주석 또는 토큰으로 변환되지 않는 전처리 토큰은 예외입니다.
  • 식별자, 주석, 문자열 리터럴, 문자 상수 또는 헤더 이름에 잘못된 멀티 바이트 문자가 포함되어 있거나 초기 시프트 상태에서 시작 및 종료되지 않습니다.
  • 동일한 식별자가 동일한 번역 단위에 내부 및 외부 연결을 모두 포함합니다.

이러한 특정 경우는 컴파일러 감지 할 수 있는 것입니다. 위원회가 모든 구현에 동일한 행동을 부과하기를 원치 않았거나 부과 할 수 없었기 때문에 그들의 행동이 정의되지 않았다고 생각합니다. 그리고 허용되는 행동 범위를 정의하는 것은 노력할 가치가 없었습니다. 그것들은 실제로 "실행되지 않을 코드"의 범주에 속하지는 않지만 여기에서 완전성을 위해 언급했습니다.


문서 에서는 섹션 2.6에서이 질문에 대해 설명합니다.

int main(void){
      guard();
      5 / 0;
}

저자는 프로그램이 guard()종료되지 않을 때 정의 된 것으로 간주합니다 . 또한 "정적으로 정의되지 않은"및 "동적으로 정의되지 않은"개념을 구별합니다. 예 :

표준 11 의 의도 는 일반적으로 상황에 대한 코드를 생성하는 것이 쉽지 않은 경우 정적으로 정의되지 않은 상황으로 만들어지는 것으로 보입니다. 코드를 생성 할 수있을 때만 상황이 동적으로 정의되지 않을 수 있습니다.

11) 위원과의 개인 서신.

나는 전체 기사를 보는 것이 좋습니다. 함께 촬영하면 일관된 그림을 그립니다.

기사의 저자가위원회 구성원과 질문에 대해 논의해야했다는 사실은 표준이 현재 귀하의 질문에 대한 답변에 모호하다는 것을 확인시켜줍니다.


이 경우 정의되지 않은 동작은 코드를 실행 한 결과입니다. 따라서 코드가 실행되지 않으면 정의되지 않은 동작이 없습니다.

정의되지 않은 동작이 코드 선언의 결과 인 경우 실행되지 않은 코드는 정의되지 않은 동작을 호출 할 수 있습니다 (예 : 변수 섀도 잉의 일부 사례가 정의되지 않은 경우).


이 답변의 마지막 단락으로 가겠습니다. https://stackoverflow.com/a/18384176/694576

... UB는 컴파일 타임 문제가 아니라 런타임 문제입니다 ...

따라서 UB가 호출되지 않았습니다.


표준이 주요 변경 사항을 만들고 코드가 갑자기 "절대 실행되지"않을 때만 가능합니다. 그러나 이것이 '정의되지 않은 동작'을 유발할 수있는 논리적 방법은 없습니다. 아무것도 일으키지 않는다 .


정의되지 않은 행동의 주제에서 공식적인 측면과 실제적인 측면을 분리하는 것은 종종 어렵습니다. 다음은 1989 표준에서 정의되지 않은 동작의 정의입니다 (최신 버전은 없지만 이것이 크게 변경 될 것으로 예상하지 않습니다).

정의되지 않은 동작 1 개
  이식 불가능하거나 오류가있는 프로그램 구성을 사용하거나
  이 국제 표준이 요구 사항을 부과하지 않는 잘못된 데이터
2 참고 가능한 정의되지 않은 동작은 상황을 완전히 무시하는 것에서부터 다양합니다.
  예측할 수없는 결과, 번역 또는 프로그램 실행 중에 작동
  문서화 된 방식으로 환경의 특성 (유무에 관계없이
  진단 메시지 발행), 번역 종료 또는
  실행 (진단 메시지 발행).

공식적인 관점에서 저는 여러분의 프로그램이 정의되지 않은 동작을 호출한다고 말하고 싶습니다. 즉, 표준이 0으로 나누기를 포함하기 때문에 실행될 때 수행 할 작업에 대한 요구 사항이 전혀 없음을 의미합니다.

반면에 실용적인 관점에서 직관적으로 예상 한대로 작동하지 않는 컴파일러를 발견하면 놀랄 것입니다.


표준은 제가 기억하는대로 규칙이 깨지는 순간부터 무엇이든 할 수 있다고 말합니다. 어쩌면 일종의 글로벌 풍미를 가진 특별한 이벤트가있을 수 있습니다 (하지만 그런 것에 대해 듣거나 읽은 적이 없습니다) ... 그래서 저는 말할 것입니다 : 아니오 이것은 UB가 될 수 없습니다. 행동이 잘 정의 된 한 0은 항상 false이므로 런타임시 규칙이 깨지지 않습니다.


여전히 정의되지 않은 행동이라고 생각하지만 표준에서 나를지지하거나 거부 할 증거를 찾을 수 없습니다.

프로그램이 정의되지 않은 동작을 호출하지 않는다고 생각합니다.

결함 보고서 # 109 는 유사한 질문을 다루며 다음과 같이 말합니다.

또한 주어진 프로그램의 모든 가능한 실행이 정의되지 않은 동작을 초래한다면 주어진 프로그램은 엄격하게 준수하지 않습니다. 준수 구현은 단순히 해당 프로그램의 일부 가능한 실행으로 인해 정의되지 않은 동작이 발생하기 때문에 엄격하게 준수하는 프로그램을 변환하는 데 실패해서는 안됩니다. foo는 절대 호출되지 않을 수 있기 때문에 주어진 예제는 준수 구현에 의해 성공적으로 번역되어야합니다.


"정의되지 않은 동작"이라는 표현이 정의 된 방법과 명령문의 "정의되지 않은 동작"이 프로그램의 "정의되지 않은 동작"과 동일한 지 여부에 따라 다릅니다.

이 프로그램은 C처럼 보이므로 컴파일러에서 사용하는 C 표준 (일부 답변과 마찬가지로)에 대한 심층 분석이 적절합니다.

지정된 표준이없는 경우 정답은 "따라 다릅니다"입니다. 일부 언어에서는 컴파일러가 추측 한 바에 따라 첫 번째 오류 이후 컴파일러가 프로그래머가 의미하는 바를 추측하고 여전히 일부 코드를 생성합니다. 다른 순수한 언어에서는 어떤 것이 정의되지 않으면 정의되지 않은 것이 전체 프로그램에 전파됩니다.

다른 언어에는 "제한된 오류"라는 개념이 있습니다. 제한된 종류의 오류에 대해 이러한 언어는 오류로 인해 발생할 수있는 손상 정도를 정의합니다. 특히 묵시적 가비지 콜렉션을 사용하는 특정 언어는 오류가 입력 시스템을 무효화하는지 여부에 관계없이 자주 차이를 만듭니다.

참고 URL : https://stackoverflow.com/questions/18385020/can-code-that-will-never-be-executed-invoke-undefined-behavior