미정의 동작은 표준이 결과를 정하지 않은 실행이다. 크래시가 날 수도 있고, 조용히 잘못된 값을 낼 수도 있고, 컴파일러가 그 경로를 없는 셈 치고 지울 수도 있다. "내 컴퓨터에서는 됐다"는 반박이 되지 못한다.
자주 만나는 미정의 동작
- 초기화하지 않은 값을 읽는다.
- 배열과 포인터를 범위 밖에서 쓴다.
- 수명이 끝난 지역 객체나 해제된 메모리를 쓴다.
- 부호 있는 정수 오버플로.
- 데이터 경합.
- 호환되지 않는 타입으로 메모리를 다시 해석한다.
const int& dangling() {
int local = 1;
return local; // 반환 뒤 local은 없다
}임시 객체에 묶인 참조가 임시보다 오래가면 같은 문제가 난다. `const int& r = Pos{}.file;`처럼 멤버만 참조하면 임시 `Pos`의 연장 규칙이 적용되지 않는 경우가 있다.
도구는 증명이다
AddressSanitizer와 UndefinedBehaviorSanitizer는 이런 실행을 테스트 중에 잡아 준다. 도구가 침묵해도 그 실행 경로를 안 탄 것일 수 있다. 경고 `-Wall -Wextra`와 정수 변환 경고를 오류에 가깝게 두고, 테스트를 그 경로까지 도달하게 만든다.
최적화는 미정의 동작이 없다고 가정한다. 오버플로 뒤에 분기하는 코드는, 오버플로가 없다는 전제 때문에 분기가 통째로 사라질 수 있다.
댓글
0개아직 댓글이 없습니다.