기타 연산자
대입·삼항·sizeof·주소·간접 참조·콤마 연산자의 평가 결과와 실제 사용 맥락을 예제로 구분합니다.
프로그램에서는 변수 값을 바꾸거나, 조건에 따라 값을 간결하게 결정하거나, 메모리/타입 정보를 확인해야 하는 상황이 자주 생깁니다.
이때 자주 쓰는 연산자들이 바로 기타 연산자입니다.
이 장에서는 대입 연산자, 복합 대입 연산자, 조건(삼항) 연산자, 크기 연산자(sizeof), 주소 연산자(&), 간접 참조 연산자(*), 콤마 연산자(,)를 순서대로 살펴봅니다.
먼저 대입 연산자(=)는 오른쪽 피연산자의 값을 왼쪽 피연산자(변수)에 할당합니다.
가장 기본적인 연산자 중 하나이며, 변수의 값을 변경하거나 초기화할 때 사용됩니다.
int age = 20; // 20을 age 변수에 대입 (초기화)
age = 21; // 21을 age 변수에 대입 (값 변경)
double pi = 3.14;
char grade = 'A';대입 연산은 오른쪽에서 왼쪽으로 진행됩니다.
대입 연산의 결과는 대입된 값 자체입니다.
이를 활용하여 여러 변수에 동시에 같은 값을 대입할 수 있습니다.
int x, y, z;
x = y = z = 10; // z에 10 대입, y에 z의 값(10) 대입, x에 y의 값(10) 대입
std::cout << "x: " << x << ", y: " << y << ", z: " << z << std::endl; // 출력: x: 10, y: 10, z: 10복합 대입 연산자
복합 대입 연산자는 산술 연산, 비트 연산 등과 대입 연산을 결합한 형태입니다.
변수 = 변수 연산 값 형태의 코드를 변수 연산= 값 형태로 간결하게 줄여줍니다.
이는 코드의 가독성을 높이고, 컴파일러가 최적화를 수행하는 데 도움을 줄 수 있습니다.
| 연산자 | 예시 | 의미 |
|---|---|---|
+= | a += b; | a = a + b; |
-= | a -= b; | a = a - b; |
*= | a *= b; | a = a * b; |
/= | a /= b; | a = a / b; |
%= | a %= b; | a = a % b; |
&= | a &= b; | a = a & b; |
|= | a |= b; | a = a | b; |
^= | a ^= b; | a = a ^ b; |
<<= | a <<= b; | a = a << b; |
>>= | a >>= b; | a = a >> b; |
#include <iostream>
int main() {
int score = 100;
score += 50; // score = score + 50; -> score는 150
std::cout << "score: " << score << std::endl;
double price = 100.0;
price *= 0.8; // price = price * 0.8; -> price는 80.0
std::cout << "price: " << price << std::endl;
int flags = 0b0010; // 십진수 2
flags |= 0b0100; // flags = flags | 0b0100; -> flags는 0b0110 (십진수 6)
std::cout << "flags: " << flags << std::endl;
return 0;
}조건(삼항) 연산자 (? :)
조건 연산자(Conditional Operator)는 유일한 삼항(ternary) 연산자로, 세 개의 피연산자를 가집니다.
특정 조건에 따라 두 값 중 하나를 선택하는 데 사용됩니다.
if-else 문을 간결하게 한 줄로 표현할 때 유용합니다.
조건 ? 표현식1 : 표현식2;조건이true이면표현식1이 평가되고 그 결과가 반환됩니다.조건이false이면표현식2가 평가되고 그 결과가 반환됩니다.
#include <iostream>
#include <string>
int main() {
int age = 19;
std::string status = (age >= 18) ? "성인" : "미성년자";
std::cout << "당신은 " << status << "입니다." << std::endl; // 출력: 당신은 성인입니다.
int num1 = 10;
int num2 = 20;
int max_val = (num1 > num2) ? num1 : num2;
std::cout << "두 수 중 큰 값: " << max_val << std::endl; // 출력: 20
// 조건 연산자의 결과를 변수에 할당하지 않고 바로 사용할 수도 있습니다.
std::cout << "시험 결과: " << (85 >= 60 ? "합격" : "불합격") << std::endl; // 출력: 합격
return 0;
}조건 연산자는 간결하지만 너무 복잡한 조건이나 표현식을 사용하면 가독성이 떨어질 수 있으므로 적절히 사용하는 것이 중요합니다.
크기 연산자 (sizeof)
sizeof 연산자는 특정 데이터 타입 또는 변수가 메모리에서 차지하는 바이트(byte) 크기를 반환합니다.
컴파일 시점에 결정되며, 주로 메모리 관리가 중요한 시스템 프로그래밍이나 데이터 구조의 크기를 파악할 때 사용됩니다.
sizeof(타입_이름);
sizeof(변수_이름);괄호를 생략할 수도 있지만, 타입을 인자로 사용할 때는 필수로 붙여야 합니다.
#include <iostream>
int main() {
int myInt;
double myDouble;
char myChar;
bool myBool;
std::cout << "int의 크기: " << sizeof(int) << " 바이트" << std::endl;
std::cout << "myInt 변수의 크기: " << sizeof(myInt) << " 바이트" << std::endl;
std::cout << "double의 크기: " << sizeof(double) << " 바이트" << std::endl;
std::cout << "myDouble 변수의 크기: " << sizeof(myDouble) << " 바이트" << std::endl;
std::cout << "char의 크기: " << sizeof(char) << " 바이트" << std::endl;
std::cout << "myChar 변수의 크기: " << sizeof(myChar) << " 바이트" << std::endl;
std::cout << "bool의 크기: " << sizeof(bool) << " 바이트" << std::endl;
std::cout << "myBool 변수의 크기: " << sizeof(myBool) << " 바이트" << std::endl;
// 배열의 크기
int numbers[5];
std::cout << "numbers 배열의 전체 크기: " << sizeof(numbers) << " 바이트" << std::endl; // 5 * sizeof(int)
std::cout << "numbers 배열의 요소 개수: " << sizeof(numbers) / sizeof(numbers[0]) << std::endl; // 배열 길이 계산
return 0;
}sizeof는 배열의 전체 크기를 구할 때 매우 유용하며, 이를 통해 배열의 요소 개수를 안전하게 계산할 수 있습니다.
주소 연산자 (&)
주소 연산자(&, Address-of Operator)는 변수의 메모리 주소를 반환합니다.
이 연산자는 포인터(Pointer)와 함께 사용됩니다.
포인터는 변수의 메모리 주소를 저장하는 특별한 타입의 변수입니다.
포인터에 대한 자세한 내용은 나중에 별도로 다시 다룰 것입니다.
&변수_이름;#include <iostream>
int main() {
int value = 123;
double pi = 3.14;
std::cout << "value 변수의 값: " << value << std::endl;
std::cout << "value 변수의 메모리 주소: " << &value << std::endl; // 16진수 주소 출력
std::cout << "pi 변수의 값: " << pi << std::endl;
std::cout << "pi 변수의 메모리 주소: " << &pi << std::endl;
return 0;
}출력되는 메모리 주소는 실행할 때마다 달라질 수 있으며, 16진수 형태로 표시됩니다.
간접 참조 연산자 (*)
간접 참조 연산자(*, Dereference Operator / Indirection Operator)는 포인터 변수가 가리키는 메모리 주소에 저장된 실제 값에 접근할 때 사용됩니다.
역시 포인터 개념과 밀접하게 연관됩니다.
*포인터_변수;#include <iostream>
int main() {
int data = 100;
int* ptr = &data; // ptr은 data의 메모리 주소를 저장하는 포인터 변수
std::cout << "data의 값: " << data << std::endl; // 출력: 100
std::cout << "ptr에 저장된 주소: " << ptr << std::endl; // data의 주소 출력
std::cout << "ptr이 가리키는 값: " << *ptr << std::endl; // 출력: 100 (*ptr은 data와 동일)
*ptr = 200; // ptr이 가리키는 메모리(즉, data)의 값을 200으로 변경
std::cout << "data의 새로운 값: " << data << std::endl; // 출력: 200
return 0;
}* 기호는 포인터 변수를 선언할 때(int* ptr;)는 포인터 타입임을 나타내고, 포인터 변수 앞에 사용될 때는 간접 참조를 나타내므로 혼동하지 않도록 주의해야 합니다.
포인터 코드는 값 칸과 주소 칸을 구분해서 읽어야 합니다. 같은 기호라도 선언과 식에서 의미가 달라집니다.
- value 칸의 주소표
읽는다.
- 0x1000 같
주소값이 나온다.
- p 안에 저장된 주소 0x1000
따라간다.
- 도착한 value 칸의 실제 값
읽거나 바꾼다.
값을 바꾸는 연산자, 메모리 주소를 확인하는 연산자, 포인터가 가리키는 값을 읽고 쓰는 연산자의 역할을 한 번 더 구분해 봅시다.
기타 연산자를 읽을 때 먼저 “변수 값이 바뀌는가, 주소를 얻는가, 주소가 가리키는 값을 읽거나 쓰는가”를 분리합니다.
- 변수에 새 값을 저장
assignment 오른쪽 표현식을 계산한 뒤 왼쪽 변수의 값을 바꿉니다. value = 9;
- 변수의 주소를 얻음
address-of & 변수가 저장된 위치를 포인터 값으로 꺼냅니다. int* p = &value;
- 주소가 가리키는 값 접근
dereference * 포인터를 따라가 실제 값을 읽거나 그 위치의 값을 바꿉니다. *p = 20;
- value
변수 실제 정수 값이 저장된 메모리 칸입니다.
- p
포인터 &value 로 주소를 저장하고, *p 로 그 칸을 다시 봅니다.
콤마 연산자 (,)
콤마 연산자(Comma Operator)는 두 개 이상의 표현식을 순차적으로 평가하고, 가장 마지막 표현식의 결과를 반환합니다.
주로 for 반복문 헤더에서 여러 초기화나 증감식을 한 줄에 작성할 때 사용됩니다.
표현식1, 표현식2, 표현식3, ... , 마지막_표현식;#include <iostream>
int main() {
int x = 10, y = 20; // 변수 선언 시 콤마 사용 (연산자 아님)
// 콤마 연산자 사용 (표현식)
int result = (x++, y += 5, x + y); // 1. x를 11로 증가, 2. y를 25로 증가, 3. (11 + 25) 계산
// 최종적으로 (x + y)의 결과인 36이 result에 대입
std::cout << "x: " << x << ", y: " << y << ", result: " << result << std::endl; // 출력: x: 11, y: 25, result: 36
// for 문에서 활용
for (int i = 0, j = 10; i < 5; ++i, --j) {
std::cout << "i: " << i << ", j: " << j << std::endl;
}
/* 출력:
i: 0, j: 10
i: 1, j: 9
i: 2, j: 8
i: 3, j: 7
i: 4, j: 6
*/
return 0;
}콤마 연산자는 매우 낮은 우선순위를 가지므로, 다른 연산자와 함께 사용할 때는 괄호로 명확히 하는 것이 좋습니다.
아래 다이어그램은 대입, 조건, sizeof, 주소, 간접 참조, 콤마 연산자를 값 변경과 메모리 접근 관점으로 구분한 역할 구조입니다.
대입, 조건, 크기, 포인터, 콤마 연산자는 값 변경, 메모리 접근, 평가 단서로 나누면 빠르게 판별됩니다.
- 값을 바꾸거나 고른다
= += ?: 오른쪽 계산 결과를 저장하거나, 조건에 따라 반환할 값을 하나 선택합니다.
- 주소와 실제 값을 오간다
& * 주소를 얻거나, 포인터가 가리키는 위치의 실제 값에 접근합니다.
- 크기와 평가 순서를 읽는다
sizeof , 바이트 크기나 왼쪽에서 오른쪽으로 평가되는 순서를 코드에 남깁니다.
| 질문 | 연산자 | 예시 | 읽는 법 |
|---|---|---|---|
| 변수 값이 바뀌는가 | = , += | sum += delta; | 기존 값을 읽고 계산 결과를 다시 저장합니다. |
| 둘 중 하나를 고르는가 | ?: | ok ? a : b | 조건 결과에 따라 반환값 하나만 선택합니다. |
| 위치를 다루는가 | & , * | p = &data; *p = 200; | 주소를 얻고, 그 주소의 실제 값을 읽거나 씁니다. |
| 값보다 단서를 보는가 | sizeof , , | sizeof(arr) , x++, y | 크기 또는 평가 순서가 핵심입니다. |
기타 연산자는 문법 자체는 짧지만, 타입 변환과 메모리 의미가 함께 숨어 있는 경우가 많아 사용 경계를 정리해두는 편이 좋습니다.
컴파일러가 해석할 수 있어도 독자가 바로 읽지 못하면 좋은 식이 아닙니다. 괄호와 임시 변수는 의도를 남기는 도구입니다.
- 산술
비교가 한 줄에 섞임
- 무엇
먼저 계산하는가?
- 비교 전 값
이름 있는 변수로 둔다.
- score > limit
- 조건 연산자
길어짐
- 두 선택지
대칭인가?
- if 문이나 함수
분리한다.
- 조건, 참, 거짓 경로
따로 보인다.
- 증감
참조가 같이 있음
- 값을 읽는가, 바꾸는가?
- 상태 변경 문장
따로 둔다.
- 부작용 위치
한 줄에 하나만 남는다.