C++에는 잘 알려지지 않았지만 활용도가 매우 높은 기능과 트릭이 많이 숨어 있습니다. 이런 기법들을 미리 익혀 두면 경쟁 프로그래밍 대회나 실무 개발에서 코드 작성 시간을 크게 단축할 수 있습니다. 지금부터 실전에서 바로 써먹을 수 있는 C++의 숨겨진 기능들을 하나씩 살펴보겠습니다.
% 연산자 없이 홀수·짝수 판별하기
비트 AND(&) 연산을 이용하면 나머지 연산자 없이도 홀수와 짝수를 구분할 수 있습니다. 모든 홀수는 최하위 비트(LSB)가 1이므로, 숫자를 1과 AND 연산했을 때 결과가 0이 아니면 홀수, 0이면 짝수입니다.
if ((n & 1) != 0) {
// 홀수
} else {
// 짝수
}
시프트 연산자로 빠르게 곱셈·나눗셈하기
2의 거듭제곱(2ⁿ)으로 곱하거나 나눌 때는 시프트 연산자가 일반 곱셈·나눗셈보다 훨씬 빠릅니다. 왼쪽으로 n비트 시프트하면 2ⁿ을 곱한 것과 같고, 오른쪽으로 n비트 시프트하면 2ⁿ으로 나눈 것과 같습니다.
x = 40; y = x << 2; // x에 4를 곱한 것과 같음, y = 160 cout << y; x = 40; y = x >> 2; // x를 4로 나눈 것과 같음, y = 10 cout << y;
임시 변수 없이 두 변수 값 교환하기
+와 - 연산자를 사용하는 방법도 있지만, 비트 XOR 연산을 활용하면 세 번째 변수 없이도 두 값을 깔끔하게 맞바꿀 수 있습니다.
// x와 y 교환 x ^= y; y ^= x; x ^= y;
strlen() 없이 문자열 순회하기
strlen() 함수를 사용할 수 없는 상황이라도 걱정할 필요가 없습니다. 문자열 길이를 미리 구하지 않고, 특정 위치의 문자가 널 문자('\0')인지만 검사하면 됩니다. 유효한 문자(0이 아닌 값)인 동안 계속 순회하고, 널 문자를 만나면 자동으로 멈춥니다.
for (int i = 0; s[i]; i++) {
cout << s[i];
}
push_back() 대신 emplace_back() 사용하기
vector 같은 컨테이너에 새 요소를 추가할 때 흔히 push_back()을 사용하지만, emplace_back()을 쓰면 훨씬 빠릅니다. emplace_back()은 별도의 임시 메모리를 할당하지 않고 컨테이너 내부에서 곧바로 객체를 생성하기 때문에 불필요한 복사 비용이 줄어듭니다.
내장 GCD(최대공약수) 함수 활용하기
C++은 최대공약수를 구하는 내장 함수를 제공합니다. 유클리드 호제법을 직접 구현할 필요 없이 다음처럼 간단히 호출할 수 있습니다.
__gcd(x, y) // x와 y의 최대공약수
배열 크기의 제한 이해하기
main 함수 안에서 선언하는 지역 배열은 스택 메모리의 한계 때문에 약 10^6 수준까지만 확보할 수 있습니다. 반면 배열을 전역으로 선언하면 최대 10^7까지 선언할 수 있습니다.
로그 연산으로 최상위 자릿수 구하기
log10을 이용하면 어떤 수의 가장 앞자리 숫자도 쉽게 계산할 수 있습니다.
n = 4578; double k = log10(n); k = k - floor(k); int x = pow(10, k); // x는 최상위 자릿수(4)
반복문 없이 자릿수 개수 구하기
로그 연산을 활용하면 루프를 돌리지 않고도 한 번에 자릿수를 구할 수 있습니다.
n = 4578; int digit_count = floor(log10(n)) + 1; // 4자리
2의 거듭제곱 여부 확인하기
x가 2의 거듭제곱이면 x와 (x-1)을 AND 연산한 결과는 항상 0이 됩니다. 이 성질을 이용하면 다음과 같이 간단히 검사할 수 있습니다.
x = 1024; bool check = x && (!(x & (x - 1))); // true이면 2의 거듭제곱
all_of, any_of, none_of 조건 검사 알고리즘
C++ 표준 라이브러리는 범위 전체의 조건을 검사하는 편리한 알고리즘을 기본으로 제공합니다.
all_of(left, left + n, isPositive()); // 모든 요소가 양수인지 검사 any_of(left, left + n, isPositive()); // 하나라도 양수가 있는지 검사 none_of(left, left + n, isPositive()); // 양수가 하나도 없는지 검사
copy_n으로 요소 복사하기
copy_n 함수를 사용하면 한 컨테이너(배열)의 요소를 지정한 개수만큼 다른 곳으로 손쉽게 복사할 수 있습니다.
int src[5] = {10, 20, 30, 40, 50};
int dest[5];
copy_n(src, 5, dest);
iota로 연속된 값 생성하기
iota 알고리즘은 시작 값을 첫 요소에 대입한 뒤, 후위 증가 연산자를 적용한 값을 차례대로 채워 넣어 연속적으로 증가하는 수열을 만들어 줍니다.
int arr[5] = {0};
char str[5] = {0};
iota(arr, arr + 5, 15); // {15, 16, 17, 18, 19} 생성
iota(str, str + 5, 'A'); // {'A', 'B', 'C', 'D', 'E'} 생성
0b 접두사로 2진수 리터럴 사용하기
숫자 앞에 0b를 붙이면 해당 값이 2진수로 표현되었음을 명시할 수 있습니다.
int x = 0b1101; // x에는 13이 저장됨
and, or 같은 대체 키워드 사용하기
C++에서는 &&, || 같은 조건 연산자 대신 and, or 같은 키워드를 그대로 사용할 수 있습니다. 가독성을 높이고 싶을 때 유용합니다.
x = 8; if (x <= 10 and x > 5) cout << "True" << endl; else cout << "False" << endl; // True 출력