c++ - 코드 - 코딩 도장 함수




정의되지 않은 동작 및 시퀀스 포인트 (4)

C ++ 98 및 C ++ 03

이 대답은 이전 버전의 C ++ 표준에 대한 것입니다. 표준의 C ++ 11 및 C ++ 14 버전에는 형식적으로 '시퀀스 포인트'가 포함되어 있지 않습니다. 작업은 '이전에 순서가 지정됨'또는 '순서가 지정되지 않음'또는 '불확정 순서가 지정됨'입니다. 그물 효과는 본질적으로 동일하지만 용어가 다릅니다.

면책 조항 : 알겠습니다. 이 대답은 약간 길다. 그것을 읽는 동안 인내심을 가져라. 이미이 사실을 알고 있다면 다시 읽는다고해서 미친 짓을하지는 않습니다.

사전 요구 사항 : C ++ 표준 의 기본 지식

시퀀스 포인트 란 무엇입니까?

스탠다드에 따르면

시퀀스 시퀀스라고하는 실행 시퀀스의 특정 특정 지점에서 이전 평가의 모든 부작용 이 완료되고 후속 평가의 부작용 이 발생하지 않아야합니다. (§1.9 / 7)

부작용? 부작용은 무엇입니까?

표현의 평가는 무언가를 생산하고, 실행 환경의 상태에 변화가있는 경우 표현 (평가)에 부작용이 있다고합니다.

예 :

int x = y++; //where y is also an int

초기화 조작 이외에도 ++ 연산자의 부작용으로 인해 y 값이 변경됩니다.

여태까지는 그런대로 잘됐다. 시퀀스 포인트로 이동. comp.lang.c 작성자가 제시 한 seq-points의 대체 정의 Steve Summit :

순차 지점은 먼지가 침전되고 지금까지 보인 모든 부작용이 완전 해지는 시점입니다.

C ++ 표준에 나열된 일반적인 시퀀스 포인트는 무엇입니까?

사람들은:

  • 전체 표현 ( §1.9/16 )의 평가가 끝날 때 (완전 표현은 다른 표현의 부분 표현이 아닌 표현입니다.) 1

예 :

int a = 5; // ; is a sequence point here
  • 첫 번째 표현 ( §1.9/18 ) 2 의 평가 이후에 다음의 각각의 표현의 평가에서

    • a && b (§5.14)
    • a || b (§5.15)
    • a ? b : c (§5.16)
    • a , b (§5.18) (여기서 a, b는 쉼표 연산자이고 func(a,a++) , 쉼표 연산자가 아니며 aa++ 인수 사이의 구분 기호에 불과합니다. 따라서이 경우 동작은 정의되지 않습니다 ( a 가 기본 유형으로 간주되는 경우))
  • 함수 본문 ( §1.9/17 )에서 표현식이나 명령문을 실행하기 전에 발생하는 모든 함수 인수 (있는 경우)를 평가 한 후 함수 호출 (함수가 인라인인지 여부).

1 : 참고 : 전체 표현식의 평가에는 어휘 표현식의 일부가 아닌 하위 표현식 평가가 포함될 수 있습니다. 예를 들어 기본 인수 식을 평가하는 데 포함 된 하위 식 (8.3.6)은 기본 인수를 정의하는식이 아닌 함수를 호출하는 식에서 생성 된 것으로 간주됩니다

2 : 표시된 연산자는 5 절에서 설명하는 기본 제공 연산자입니다. 이러한 연산자 중 하나가 유효한 컨텍스트에서 오버로드 (13 절)되어 사용자 정의 연산자 함수를 지정하면 표현식은 함수 호출을 지정하고 피연산자는 암시 적 시퀀스 포인트없이 인수 목록을 형성합니다.

정의되지 않은 행동이란 무엇입니까?

이 표준은 §1.3.12 절의 정의되지 않은 행동을 §1.3.12 과 같이 정의합니다.

이 국제 표준이 요구 사항을 부과 하지 않는 잘못된 프로그램 구성이나 잘못된 데이터를 사용할 때 발생할 수있는 행동.

이 국제 표준이 행동의 명시 적 정의에 대한 설명을 생략 할 때 정의되지 않은 행동이 예상 될 수도있다.

3 : 허용되지 않는 정의 된 동작은 예측할 수없는 결과로 상황을 완전히 무시한 것, 환경의 문서화 된 방식으로 (진단 메시지 발급 여부와 상관없이) 변환 또는 프로그램 실행 중에 동작하는 것, 변환 또는 실행을 종료하는 것 (진단 메시지의 발행과 함께).

간단히 말해, 정의되지 않은 동작은 코를 벗어나는 데몬이 임신중인 여자 친구에게 일어날 수 있음을 의미합니다.

정의되지 않은 동작과 시퀀스 포인트 사이의 관계는 무엇입니까?

내가 이해하기 전에 정의되지 않은 행동, 명시 되지 않은 행동 및 구현 정의 된 행동 사이의 차이를 알아야합니다.

또한 the order of evaluation of operands of individual operators and subexpressions of individual expressions, and the order in which side effects take place, is unspecified 알아야합니다.

예 :

int x = 5, y = 6;

int z = x++ + y++; //it is unspecified whether x++ or y++ will be evaluated first.

here 또 다른 예가 here .

이제 §5/4 의 표준에 따르면

  • 1) 이전 및 다음 시퀀스 포인트 사이에서 스칼라 객체는 저장된 값을 최대 한 번 표현식을 평가하여 수정해야합니다.

무슨 뜻이에요?

비공식적으로 이는 두 시퀀스 포인트 사이에서 변수가 두 번 이상 수정되지 않아야 함을 의미합니다. 표현식 문에서 next sequence point 는 일반적으로 종료 세미콜론에 있고 previous sequence point 는 이전 명령문의 끝에 있습니다. 표현식에는 중간 sequence points 포함될 수도 있습니다.

위 문장에서 다음 표현식은 정의되지 않은 동작을 호출합니다.

i++ * ++i;   // UB, i is modified more than once btw two SPs
i = ++i;     // UB, same as above
++i = 2;     // UB, same as above
i = ++i + 1; // UB, same as above
++++++i;     // UB, parsed as (++(++(++i)))

i = (i, ++i, ++i); // UB, there's no SP between `++i` (right most) and assignment to `i` (`i` is modified more than once btw two SPs)

하지만 다음 표현식은 훌륭합니다.

i = (i, ++i, 1) + 1; // well defined (AFAIK)
i = (++i, i++, i);   // well defined 
int j = i;
j = (++i, i++, j*i); // well defined
  • 2) 또한, 이전 값은 저장 될 값을 결정하기 위해서만 액세스되어야한다.

무슨 뜻이에요? 즉, 객체가 전체 표현식 내에 작성된 경우 동일한 표현식 내에서 객체에 대한 모든 액세스 가 직접 작성되어야 합니다.

예를 들어 i = i + 1 에서 i = i + 1 모든 액세스는 (LHS와 RHS에서) 쓰여질 값의 계산직접 관련됩니다 . 그래서 괜찮습니다.

이 규칙은 합법적 인 표현을 수정하여 액세스가 명백하게 선행하는 것으로 제한합니다.

예제 1 :

std::printf("%d %d", i,++i); // invokes Undefined Behaviour because of Rule no 2

예 2 :

a[i] = i++ // or a[++i] = i or a[i++] = ++i etc

i ( i a[i] )의 액세스 중 하나가 a[i] 에 저장되는 값과 아무런 관련이 없기 때문에 ( i++ 에서 발생 함) 정의 할 수있는 좋은 방법이 없기 때문에 허용되지 않습니다. 우리의 이해 나 컴파일러를 위해 - 증분 값이 저장되기 전이나 후에 액세스가 수행되어야하는지 여부. 그래서 그 행동은 정의되지 않았습니다.

예 3 :

int x = i + i++ ;// Similar to above

C ++ 11에 대한 후속 답변.

"시퀀스 포인트"란 무엇입니까?

정의되지 않은 동작과 시퀀스 포인트 사이의 관계는 무엇입니까?

저는 종종 a[++i] = i; 와 같이 우스꽝스럽고 복잡한 표현을 사용합니다 a[++i] = i; 기분을 좋게하기 위해서. 왜 내가 그걸 사용하지 말아야합니까?

이 글을 읽었다면 후속 질문 인 정의되지 않은 행동과 순서 지점을 다시 읽으십시오.

(참고 :이 항목은 Stack Overflow의 C ++ FAQ에 대한 항목입니다.이 양식에 FAQ를 제공한다는 아이디어를 비판하고 싶다면이 모든 것을 시작한 메타에 게시 하면됩니다. 그 질문은 C ++ 대화방 에서 모니터링됩니다. FAQ 은 처음에 FAQ 아이디어가 시작 되었기 때문에 아이디어가 떠오른 사람들이 대답을 읽을 가능성이 높습니다.)


나는 변화에 대한 근본적인 이유가 있다고 추측하고있다. 오래된 해석을 더 명확하게하는 것은 단지 화장품이 아니다. 그 이유는 동시성이다. 명시되지 않은 정교화 된 순서는 몇 가지 가능한 연속 된 순서 중 하나를 선택하는 것일 뿐이며, 이는 주문 전후에 상당히 다릅니다. 왜냐하면 지정된 순서가 없으면 이전의 규칙을 따르지 않기 때문에 동시 평가가 가능하기 때문입니다. 예 :

f (a,b)

이전에 b, 또는 b를 누른 다음 a. 이제 a와 b는 인터리브 된 명령이나 다른 코어에서도 평가할 수 있습니다.


C ++ 17 ( N4659 )에는 표현 평가 의 엄격한 순서를 정의하는 관용적 C ++ 용 표현식 평가 순서 구체화 제안이 포함되어 있습니다.

특히 다음 문장 이 추가되었습니다.

8.18 배정 및 복합 할당 연산자 :
....

모든 경우에 할당은 오른쪽 및 왼쪽 피연산자의 값 계산 후 및 할당 표현식의 값 계산 전에 순서가 지정됩니다. 오른쪽 피연산자는 왼쪽 피연산자보다 먼저 배열됩니다.

문제의 하나를 포함하여 이전에 정의되지 않은 동작의 여러 사례를 유효하게 만듭니다.

a[++i] = i;

그러나 몇 가지 다른 유사한 경우 여전히 정의되지 않은 동작으로 이어집니다.

N4140 :

i = i++ + 1; // the behavior is undefined

하지만 N4659

i = i++ + 1; // the value of i is incremented
i = i++ + i; // the behavior is undefined

물론 C ++ 17 호환 컴파일러를 사용한다고해서 그러한 표현을 작성해야한다는 것을 의미하지는 않습니다.


이것은 이전 답변에 대한 후속 조치이며 C ++ 11 관련 자료를 포함합니다. .

전제 조건 : 관계 (수학)의 기본 지식.

C ++ 11에 시퀀스 포인트가 없다는 것이 사실입니까?

예! 이것은 사실입니다.

Sequence Points 는 C ++ 11에서 Sequenced BeforeSequenced After (및 Unsequenced and Indeterminately Sequenced ) relations 로 대체되었습니다.

이것은 정확히 'Sequenced before'것인가?

Sequenced Before (§1.9 / 13) 는 다음과 같은 관계입니다.

단일 thread 실행 한 평가와 엄격한 부분 순서 유도 1 사이

공식적으로 이것은 두 가지 평가 (아래 참조)가 주어 졌음을 의미합니다 (아래 참조) A AB 보다 먼저 시퀀싱 되면 A 의 실행이 B 의 실행 보다 먼저 수행됩니다. AB 보다 먼저 배열되지 않고 BA 보다 먼저 배열되지 않으면 AB 는 순서가 지정되지 않습니다 2 .

평가 ABB 또는 BA 보다 먼저 배열되기 전에 A 가 순서가 정해 지면 불확실하게 순서정해 지지만, 어느 것이 3 인지는 불확실하다 .

[노트]
1 : 엄격한 부분 순서는 집합 P 대해 Asymmetric 적이고 이항 Asymmetric 되는 이진 관계 "<" . 즉, P 모든 a , bc 에 대해 다음과 같이한다.
........(나는). a <b then ¬ (b <a) ( asymmetry );
........ (ii). a <b와 b <c이면 a <c ( transitivity ).
2 : 순서없는 평가 의 실행이 중복 될 수 있습니다.
3 : 불확실하게 서열화 된 평가중첩 될 수 없지만 어느 것이 먼저 실행될 수 있습니다.

C ++ 11의 맥락에서 '평가'라는 단어의 의미는 무엇입니까?

C ++ 11에서 표현식 (또는 하위 표현식)의 평가는 일반적으로 다음을 포함합니다 :

  • 값 계산 ( glvalue 평가를 위해 객체의 신원을 결정하고 이전 값 평가 를 위해 객체에 이전에 지정된 값을 가져 오는 작업 포함) 및

  • 부작용의 개시.

지금 (§1.9 / 14)

전체 표현식과 관련된 모든 값 계산 및 부작용 은 평가할 다음 전체 표현식 과 관련된 모든 값 계산 및 부작용이 발생 하기 전에 순서 가 지정됩니다 .

  • 간단한 예 :

    int x; x = 10; ++x;

    ++x 와 연관된 값 계산 및 부작용은 값 계산 및 x = 10; 의 부작용 후에 순서화됩니다 x = 10;

그러므로 정의되지 않은 행동과 위에서 언급 한 것들 사이에는 어떤 관계가 있어야합니다. 맞습니까?

예! 권리.

(§1.9 / 15)에서

명시된 경우를 제외하고 개별 연산자의 피연산자와 개별 표현식의 하위 표현식에 대한 평가는 순서가 지정 되지 않습니다 4 .

예 :

int main()
{
     int num = 19 ;
     num = (num << 3) + (num >> 3);
} 
  1. + 연산자의 피연산자 평가는 서로 상대적으로 순서가 지정되지 않습니다.
  2. <<>> 연산자의 피연산자 평가는 서로 상대적으로 순서가 지정되지 않습니다.

4 : 프로그램 실행 중에 한 번 이상 평가되는 표현식에서 하위 표현식의 순서가 지정 되지 않은 순서로 정렬 되지 않은 평가는 다른 평가에서 일관되게 수행 될 필요가 없습니다.

(§1.9 / 15) 연산자의 피연산자 값 계산은 연산자 결과 값 계산 전에 순서가 지정됩니다.

이는 x + y 에서 xy 의 값 계산이 (x + y) 의 값 계산 전에 순서가 정해진다는 것을 의미합니다.

더 중요한 것은

(§1.9 / 15) 스칼라 객체에 대한 부작용이

(a) 동일한 스칼라 객체에 대한 또 다른 부작용

또는

(b) 동일한 스칼라 객체의 값을 사용하는 값 ​​계산.

동작은 정의되지 않습니다 .

예 :

int i = 5, v[10] = { };
void  f(int,  int);
  1. i = i++ * ++i; // Undefined Behaviour
  2. i = ++i + i++; // Undefined Behaviour
  3. i = ++i + ++i; // Undefined Behaviour
  4. i = v[i++]; // Undefined Behaviour
  5. i = v[++i]: // Well-defined Behavior
  6. i = i++ + 1; // Undefined Behaviour
  7. i = ++i + 1; // Well-defined Behaviour
  8. ++++i; // Well-defined Behaviour
  9. f(i = -1, i = -1); // Undefined Behaviour (see below)

함수를 호출 할 때 (함수가 인라인인지 여부에 관계없이), 인수 표현식 또는 호출 된 함수를 지정하는 후위 표현식과 관련된 모든 값 계산 및 부작용은 해당 함수의 본문에있는 모든 표현식이나 명령문이 실행되기 전에 순서가 지정됩니다. 호출 된 함수. [ 참고 : 다른 인수 표현식과 관련된 값 계산 및 부작용은 순서가 지정되지 않습니다 . - 끝말 ]

표현식 (5) , (7)(8) 은 정의되지 않은 동작을 호출하지 않습니다. 자세한 설명은 다음 답변을 확인하십시오.

최종 참고 사항 :

게시물에 결함이 있으면 주석을 남겨주세요. 파워 유저 (rep> 20000)는 오타 및 기타 실수를 수정하기 위해 포스트를 편집하는 것을 망설이지 마십시오.





sequence-points