C/C++ 프로그래밍에서 + 연산자와 같은 산술 연산자를 사용하지 않고 두 정수의 합을 구해야 하는 경우가 있습니다. 면접 문제로도 자주 등장하는 이 주제는 크게 두 가지 방법으로 해결할 수 있습니다.
- 포인터(pointer) 활용
- 비트 연산자(bitwise operator) 활용
각각의 방법을 예제 코드와 함께 자세히 살펴보겠습니다.
방법 1: 포인터를 이용한 덧셈
배열 인덱싱의 원리를 활용하면 산술 연산자 없이 덧셈을 수행할 수 있습니다. 배열에서 p[b]는 포인터 p로부터 b개의 요소만큼 떨어진 주소를 의미합니다. 즉, 내부적으로 포인터에 오프셋이 더해지는 원리를 역으로 이용하는 것입니다.
#include <iostream>
using namespace std;
int sum(int a, int b){
int *p = &a;
return (int)&p[b];
}
int main() {
int add = sum(2, 3);
cout << add << endl;
return 0;
}
출력 결과
5
동작 원리: 포인터 p가 변수 a의 주소를 가리키고 있을 때, &p[b]는 그 주소에서 b만큼 떨어진 위치의 주소를 반환합니다. 이때 각 int 요소의 크기(4바이트)가 곱해져 실질적으로 a + b와 같은 효과를 얻게 됩니다.
참고: 이 방법은 컴파일러나 플랫폼에 따라 동작이 보장되지 않는 비표준(unspecified behavior) 기법입니다. 학습 목적으로 이해하고, 실제 프로덕션 코드에서는 비트 연산 방식을 사용하는 것이 좋습니다.
방법 2: 비트 연산자를 이용한 덧셈
더 안정적이고 널리 사용되는 방법은 XOR(^)과 AND(&) 비트 연산자를 조합하는 것입니다. 이진수 덧셈의 원리를 그대로 구현한 방식입니다.
- XOR(
^): 올림수(carry)를 제외한 자리별 합을 계산합니다. - AND(
&): 올림수가 발생하는 자릿수를 찾아낸 후, 왼쪽 시프트(<< 1)하여 다음 자리로 넘깁니다. - 올림수가 0이 될 때까지 위 과정을 재귀적으로 반복합니다.
#include <iostream>
using namespace std;
int sum(int a, int b){
int s = a ^ b; // 올림수 없는 자리별 합
int carry = a & b; // 올림수 계산
if (carry == 0)
return s;
else
return sum(s, carry << 1); // 올림수를 한 자리 왼쪽으로 이동 후 재귀 호출
}
int main() {
int add = sum(2, 3);
cout << add << endl;
return 0;
}
출력 결과
5
실행 과정 예시: sum(2, 3)
2(010)와3(011)의 XOR 결과는1(001), AND 결과는2(010)- 올림수가 존재하므로
sum(1, 4)재귀 호출 1(001)와4(100)의 XOR 결과는5(101), AND 결과는0- 올림수가 0이므로 최종 결과 5 반환
마무리
포인터 방식은 개념적으로 흥미롭지만 플랫폼 의존성이 있어 권장되지 않으며, 반면 비트 연산자를 활용한 재귀 방식은 하드웨어 덧셈기의 동작 원리를 그대로 반영하기 때문에 어떤 환경에서도 안정적으로 동작합니다. 음수 처리 역시 이진수 보수(two's complement) 표현 덕분에 동일한 로직으로 올바르게 계산됩니다. 알고리즘 면접 대비나 저수준 프로그래밍 이해를 위해 두 가지 방법 모두 기억해 두면 유용합니다.