제목: “이번 주의 팁 #227: 빈 컨테이너와 부호 없는 정수 연산 주의하기”

원문 게시일: 2023년 11월 16일
업데이트: 2024년 3월 11일

작성자: James Dennett

빠른 링크: abseil.io/tips/227


인덱스 기반 반복문: 여전히 유용한 경우가 있다

범위 기반 for 루프가 등장하면서 인덱스 기반 for 루프는 현대 C++ 코드에서 덜 자주 사용됩니다. 그러나 여전히 인덱스가 필요한 경우가 있습니다. 예를 들어, 여러 컨테이너를 병렬로 순회하거나, 하나의 컨테이너에서 인접한 여러 요소를 처리하려는 경우입니다. 이번 팁에서는 두 번째 경우에 발생할 수 있는 문제를 살펴봅니다.


문제 있는 코드

다음은 올바를 것처럼 보이는 코드입니다:

CPP
for (int64_t i = 0; i < v.size() - 1; ++i) {
  ProcessPair(v[i], v[i+1]);
}
클릭하여 더 보기

이 코드는 ProcessPair()를 호출하기 전에 유효한 인덱스를 확인하는 것처럼 보입니다. 하지만 이 코드는 “올바를 것처럼 보일 뿐"입니다. 예를 들어, 컨테이너 v가 비어 있는 경우를 고려하지 않은 것입니다. 만약 v가 비어 있는 상태로 이 루프가 실행된다면, C++는 예기치 않은 동작을 일으킬 수 있습니다.


부호 없는 정수의 문제

C++ 표준에서는 컨테이너 크기를 나타내기 위해 부호 없는 정수 타입을 사용합니다 (v.size()unsigned 타입). 그러나 부호 없는 정수는 연산 시 문제가 발생할 수 있습니다.

예를 들어, v.size()가 0인 경우, v.size() - 1은 -1이 아니라 최대값(예: std::size_t의 경우 18446744073709551615)으로 변환됩니다. 결과적으로, 조건 i < v.size() - 1은 잘못 평가될 수 있습니다. 즉, 루프가 실행되고 v[i]와 같은 코드에서 **정의되지 않은 동작(UB)**이 발생할 수 있습니다.


올바르게 수정하기

문제를 해결하기 위해 코드의 의도를 더 명확히 표현하면 문제가 사라집니다.

의도를 더 직접적으로 표현하기

루프 조건의 목적은 ii + 1이 모두 유효한 인덱스인지 확인하는 것입니다. 이를 C++로 직접적으로 번역하면 다음과 같이 표현됩니다:

새로운 조건인 i + 1 < v.size()v.size()에서 1을 빼는 연산을 피하므로, 부호 없는 정수의 오버플로를 방지합니다.


수정된 코드

수정된 코드는 다음과 같습니다:

CPP
for (int64_t i = 0; i + 1 < v.size(); ++i) {
  ProcessPair(v[i], v[i+1]);
}
클릭하여 더 보기

이 변경으로 인해:

  1. v가 비어 있을 때에도 루프가 안전하게 작동합니다.
  2. 인덱스 범위 확인이 더 명확하고 직관적으로 표현됩니다.

요약

수정된 코드는 몇 바이트만 바뀌었지만, 중요한 몇 가지 교훈을 제공합니다:

이 팁을 통해 부호 없는 정수와 관련된 오류를 방지하고 더 안전한 코드를 작성할 수 있기를 바랍니다.

라이선스

저작자: Jaehun Ryu

링크: https://jaehun.me/posts/abseil-tip-227-%EB%B9%88-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88%EC%99%80-%EB%B6%80%ED%98%B8-%EC%97%86%EB%8A%94-%EC%A0%95%EC%88%98-%EC%97%B0%EC%82%B0-%EC%A3%BC%EC%9D%98%ED%95%98%EA%B8%B0/

라이선스: CC BY 4.0

이 저작물은 크리에이티브 커먼즈 저작자표시 4.0 국제 라이선스에 따라 이용할 수 있습니다. 출처를 밝히면 상업적 목적을 포함해 자유롭게 이용 가능합니다.

댓글

검색 시작

검색어를 입력하세요

↑↓
ESC
⌘K 단축키