P.2: Write in ISO Standard C++
이유
표준 C++ 문법을 사용하면 대부분의 환경에서 정상 동작을 보장할 수 있습니다. (이식성의 향상)
알아두기
특정 환경에서 동작하는 코드를 작성하거나 (예시로 windows.h 등) 시스템 리소스를 직접 사용하기 위해 확장 문법이 꼭 필요한 경우도 있습니다. (예시로 항공기 제어 프로그램 설계를 위한 동적 메모리 할당 금지 등)
이때는 가능한 한 확장 문법의 사용을 분리 (캡슐화) 하고 다른 시스템에서 확장을 지원하지 않을 경우에 쉽게 끄고 켤 수 있도록 컴파일 환경과 인터페이스를 구축하는 것이 좋습니다.
확장 문법은 표준 문법과 다르게 동작이 엄격하게 정의되지 않은 경우가 많아서 컴파일 시스템 (환경) 에 따라 동작이 다를 수 있고 이는 이식성을 저하시키는 결과로 이어집니다.
알아두기
물론 ISO 표준 C++ 를 사용한다고 이식성이 반드시 보장되는 것은 아닙니다. 🤪
표준 C++ 도 다음과 같은 지뢰들이 있기 때문에 여러분이 조심해서 코드를 작성해야 합니다.
1. 확실하지 않은 동작 (Undefined behavior)
어떤 문법이 사용되었을 때 그 동작이 표준에 정의되어 있지 않아서 컴파일 에러가 날 수도 있고, 시스템마다 다르게 동작하거나 어제는 잘 동작하는데 오늘 컴파일러를 업데이트했더니 동작이 이상해질 수도 있습니다.
int foo(int x) {
// true 가 될 수도 있고, x 가 이미 int 로 표현 가능한 최대값일 때의 동작은 정의되어 있지 않습니다
return x + 1 > x;
}
// ---------- 아래는 어셈블리 코드로 변환된 예시입니다 ----------
foo(int):
// 아이쿠.. 대부분의 컴파일러는 최적화를 위해 그냥 true 처럼 처리를 해버리네요
mov eax, 1
ret
위는 확실하지 않은 동작 (undefined behavior) 의 예시입니다. 잘 생각해보면 return 의 결과가 반드시 true 가 나와야 하는 것은 아닙니다. x 가 이미 INT_MAX 값을 가지고 있다면 +1 을 하는 순간 signed overflow 가 발생하고 음수가 될 수도 있는데 이 경우 어떤 동작으로 처리할지는 ISO 표준 C++ 에서 아예 정의하고 있지 않습니다.
대부분의 컴파일러는 최적화를 위해 항상 true 를 반환하도록 처리하고 있는 것으로 보입니다만, 시스템이나 컴파일러에 따라 동작이 어떻게 바뀔 지 모르기 때문에 매우 위험한 코드이므로 이렇게 코드를 작성하면 절대 안됩니다. (많은 사람들이 이러한 실수를 합니다.)
2. 지정되지 않은 동작 (Unspecified behaviour)
확실하지 않은 동작 (Undefined behavior) 과는 다르게 컴파일이 된다는 보장이 있지만, 모든 컴파일러나 시스템에서 일관된 동작을 보장하지는 않습니다. 컴파일러의 동작을 이해하면 주의해서 사용할 수 있습니다.
int i = 0;
// +, = 는 시퀀싱 단위가 아니기 때문에 하나의 구문으로 처리되며, ++i 가 먼저 수행된다는 보장이 없습니다
i = ++i + 1;
위는 지정되지 않은 동작 (Unspecified behaviour) 의 예시입니다. 반드시 컴파일된다는 보장이 있지만, 그 동작은 명확히 정의된 바가 없습니다. 컴파일러의 특성을 이해하고 사용할 수 있지만 명확한 의미 전달을 방해하기 때문에 가능한 한 피하는 것이 좋습니다.
3. 구현 (전처리기, 컴파일러 등) 에 따라 마음대로 정해지는 동작 (Implementation-defined behavior)
어떤 문법이 사용되었을 때 컴파일러에 따라 동작이 달라질 수 있습니다. 다만 컴파일러 제조사에서 어떤 동작을 할지 설명서에 명시적으로 작성해 놓아야 한다는 차이점이 있습니다.
에시로 MSVC 컴파일러의 경우 signed int 를 -2,147,483,648 to 2,147,483,647 범위를 가지는 4 bytes 로 문서에 정의하고 있습니다.
정리
만약 ISO 표준 C++ 에 없는 확장 기능을 꼭 사용해야 한다면, 해당 부분을 인터페이스로 만들어 캡슐화해야 합니다.
1. 확실하지 않은 동작 (Undefined behavior) : 국제 표준이 요구사항을 아예 정의하지 않았고, 사용하면 안됩니다.
2. 지정되지 않은 동작 (Unspecified behaviour) : 잘 구성된 프로그램 구성 및 올바른 데이터에 대한 동작의 경우 구현에 따라 다르게 동작하도록 허용합니다.
3. 구현에 따라 마음대로 정해지는 동작 (Implementation-defined behavior) : 잘 구성된 프로그램 구성 및 올바른 데이터에 대한 동작의 경우 구현에 따라 다르게 동작하도록 허용하며, 각 구현은 반드시 컴파일러 제조사가 문서화해야 합니다.
참고자료
https://www.youtube.com/watch?v=F6zzZbEgpL4
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4659.pdf
https://stackoverflow.com/questions/1860461/why-is-i-i-1-unspecified-behavior
'C++' 카테고리의 다른 글
[C++ Core Guidelines] P.5 가급적 컴파일 타임에 코드가 평가될 수 있도록 만들자 (0) | 2024.04.11 |
---|---|
[C++ Core Guidelines] P.4 가급적 컴파일 타임 타입을 사용하자 (0) | 2024.04.07 |
[C++ Core Guidelines] P.1, P.3 생각을 코드로 직접 나타내자 (2) | 2024.03.27 |
[C++ Core Guidelines] 번역에 앞서 (1) | 2024.03.26 |
[C++] 학습시 참고 할만한 사이트 정리 (0) | 2024.03.21 |