산술 연산자

산술 연산자는 사칙연산을 다루는 연산자로 가장 기본적이면서 가장 많이 사용되는 연산자이다.

산술 연산자로는 + - * / % 가 있으며 두 개의 피연산자를 요구하는 이항 연산자이다. 산술 연산자의 우선순위는 사칙 연산의 우선 순위와 같다.

산술 연산자 설명
+ 왼쪽 피연산자에 오른쪽 피연산자를 더한다.
- 왼쪽 피연산자에서 오른쪽 피연산자를 뺀다.
* 왼쪽 피연산자에 오른쪽 피연산자를 곱한다.
/ 왼쪽 피연산자를 오른쪽 피연산자로 나눈 몫을 구한다.
% 왼쪽 피연산자를 오른쪽 피연산자로 나눈 후의 나머지를 구한다.
#include <iostream>

int main()
{
	int a = 10;
	int b = 3;

	std::cout << "a = " << a << ", b = " << b << std::endl;
	std::cout << "a + b = " << a + b << std::endl;
	std::cout << "a - b = " << a - b << std::endl;
	std::cout << "a * b = " << a * b << std::endl;
	std::cout << "a / b = " << a / b << std::endl;
	std::cout << "a % b = " << a % b << std::endl;

	return 0;
}
a = 10, b = 3
a + b = 13
a - b = 7
a * b = 30
a / b = 3
a % b = 1

증감 연산자

증감 연산자는 피연산자를 1 씩 증가 또는 감소시킬 때 사용하는 연산자로 피연산자가 하나뿐인 단항 연산자이다.

증감 연산자는 피연산자의 어느 쪽에 위치하는가에 따라 연산 순서 및 결과가 달라진다.

연산자 설명
n++ 해당 문장을 진행한 후 피연산자의 값을 1 증가시킨다.
++n 피연산자의 값을 1 증가시킨 후에 해당 문장을 진행한다.
n-- 해당 문장을 진행한 후 피연산자의 값을 1 감소시킨다.
--n 피연산자의 값을 1 감소시킨 후에 해당 문장을 진행한다.
#include <iostream>

int main()
{
	int a = 10;
	int b = 1;

	std::cout << "a = " << a << ", b = " << b << std::endl;

	a++;
	std::cout << "a = " << a << ", b = " << b << std::endl;

	++a;
	std::cout << "a = " << a << ", b = " << b << std::endl;

	a--;
	std::cout << "a = " << a << ", b = " << b << std::endl;

	--a;
	std::cout << "a = " << a << ", b = " << b << std::endl;

	std::cout << std::endl;
	std::cout << "a++ + b = " << a++ + b << std::endl; // 코드 진행 후 a = 11
	std::cout << "a = " << a << ", b = " << b << std::endl;

	std::cout << std::endl;
	std::cout << "++a + b = " << ++a + b << std::endl;
	std::cout << "a = " << a << ", b = " << b << std::endl;

	return 0;
}
a = 10, b = 1
a = 11, b = 1
a = 12, b = 1
a = 11, b = 1
a = 10, b = 1

a++ + b = 11
a = 11, b = 1

++a + b = 13
a = 12, b = 1

대입 연산자

대입 연산자는 변수에 값을 대입할 때 사용하는 이항 연산자이다.

연산자 설명
= 왼쪽 피연산자에 오른쪽 피연산자를 대입한다.

등호의 왼쪽은 반드시 변수이어야 하고 등호의 오른쪽은 어떠한 수식이라도 가능하다.

#include <iostream>

int main()
{
	int num;

	num = 5;
	std::cout << "num = " << num << std::endl;

	num = 1 + 2;
	std::cout << "num = " << num << std::endl;

	num = 1;
	num = num + 1;
	std::cout << "num = " << num << std::endl;

	return 0;
}
num = 5
num = 3
num = 2

복합 대입 연산자

복합 대입 연산자는 += 처럼 대입 연산자와 산술 연산자를 합쳐 놓은 연산자이다.

연산자 의미 연산자 의미
x += y x = x + y x &= y x = x & y
x -= y x = x - y `x = y`
x *= y x = x * y x ^= y x = x ^ y
x /= y x = x / y x >>= y x = x >> y
x %= y x = x % y x <<= y x = x << y
#include <iostream>

int main()
{
	int num = 8;

	num += 10; // num = num + 10 과 같은 의미입니다.
	std::cout << "(num += 10 의 결과) num = " << num << std::endl;

	num -= 5; // num = num - 5 과 같은 의미입니다.
	std::cout << "(num -= 5 의 결과)  num = " << num << std::endl;

	num *= 2; // num = num * 2 과 같은 의미입니다.
	std::cout << "(num *= 2 의 결과)  num = " << num << std::endl;

	num /= 5; // num = num / 5 과 같은 의미입니다.
	std::cout << "(num /= 5 의 결과)  num = " << num << std::endl;

	num %= 2; // num = num % 5 과 같은 의미입니다.
	std::cout << "(num %= 2 의 결과)  num = " << num << std::endl;
}
(num += 10 의 결과) num = 18
(num -= 5 의 결과)  num = 13
(num *= 2 의 결과)  num = 26
(num /= 5 의 결과)  num = 5
(num %= 2 의 결과)  num = 1

비교 연산자

비교 연산자는 피연산자 사이의 상대적인 크기를 판단하는 연산자로, 왼쪽 피연산자와 오른쪽 피연산자를 비교하여 어느 쪽이 더 큰지, 작은지 또는 서로 같은지를 판단한다.

연산자 설명
== 왼쪽 피연산자와 오른쪽 피연산자가 같으면 true 를 반환
!= 왼쪽 피연산자와 오른쪽 피연산자가 같지 않으면 true 를 반환
> 왼쪽 피연산자가 오른쪽 피연산자보다 크면 true 를 반환
>= 왼쪽 피연산자가 오른쪽 피연산자보다 크거나 같으면 true 를 반환
< 왼쪽 피연산자가 오른쪽 피연산자보다 작으면 true 를 반환
<= 왼쪽 피연산자가 오른쪽 피연산자보다 작거나 같으면 true 를 반환
#include <iostream>

int main()
{
	int a = 1;
	int b = 2;

	std::cout << (a == b) << std::endl;
	std::cout << (a != b) << std::endl;
	std::cout << (a > b) << std::endl;
	std::cout << (a >= b) << std::endl;
	std::cout << (a < b) << std::endl;
	std::cout << (a <= b) << std::endl;

	return 0;
}
0
1
0
0
1
1

논리 연산자

논리 연산자는 주어진 논리식을 판단하여 true, false 를 결정하는 연산자이다. AND 연산과 OR 연산은 2개의 피연산자를 갖고, NOT 연산은 1 개의 피연산자를 갖는다.

연산자 설명
&& 논리식이 모두 truetrue를 반환한다. (논리 AND 연산)
`
! 논리식의 결과가 truefalse를, falsetrue를 반환한다. (논리 NOT 연산)

논리 연산자 동작 결과 (진리표)

| A | B | A && B | A || B | !A | | --- | --- | --- | --- | --- | | true | true | true | true | false | | false | true | false | true | true | | true | false | false | true | false | | false | false | false | false | true |

#include <iostream>

int main()
{
	int a = 1;
	int b = 2;

	std::cout << ((a < 2) && (b > 1)) << std::endl; // true && true
	std::cout << ((a > 2) || (b < 1)) << std::endl; // false || false
	std::cout << ((a > 2) || (b > 1)) << std::endl; // false || true
	std::cout << !(a > 0) << std::endl; // !true

	return 0;
}
1
0
1
0

비트 연산자

비트 연산자는 비트 bit 단위로 논리 연산을 할 때 사용되는 연산자이다.

| --- | --- |

Untitled

Untitled

왼쪽 시프트 연산 << 의 경우, 양수, 음수와 관계없이 새롭게 채워지는 비트들은 0 으로 채워지고, 이동으로 인해 밀려나는 왼쪽 비트들은 버려진다.

Untitled

오른쪽 시프트 연산 >> 의 경우, 양수의 값을 이동시킬 때 새롭게 채워지는 비트들은 0 으로 채워지고, 음수의 값을 이동시킬 때 새롭게 채워지는 비트들은 1 로 채워진다. 이동으로 인해 밀려나는 오른쪽 비트들은 버려진다.

Untitled

위의 그림에서 범위를 넘어가는 비트 연산은 버려진다고 표현했지만 하드웨어에 따라 다르게 처리되므로 위처럼 범위를 넘어가는 비트 연산의 경우에는 사용하지 않는 것이 좋다.

#include <iostream>
#include <bitset>

int main()
{
	char a = 0b00001010; // 0000 1010
	char b = 0b00000011; // 0000 0011
	char c = 0b11011010; // 1101 1010

	std::cout << "    ~a : " << std::bitset<8>(~a) << std::endl;;
	std::cout << " a & b : " << std::bitset<8>(a & b) << std::endl;
	std::cout << " a | b : " << std::bitset<8>(a | b) << std::endl;
	std::cout << " a ^ b : " << std::bitset<8>(a ^ b) << std::endl;
	std::cout << "a >> 2 : " << std::bitset<8>(a >> 2) << std::endl; // 양수 오른쪽 쉬프트
	std::cout << "c >> 2 : " << std::bitset<8>(c >> 2) << std::endl; // 음수 오른쪽 쉬프트
	std::cout << "c << 2 : " << std::bitset<8>(c << 2) << std::endl;

	return 0;
}
    ~a : 11110101
 a & b : 00000010
 a | b : 00001011
 a ^ b : 00001001
a >> 2 : 00000010
c >> 2 : 11110110
c << 2 : 01101000

위의 예제들에서 보면 알 수 있듯이 << 연산자의 경우 1칸 이동할때마다 정수의 값이 2배로 커지고, >> 연산자의 경우 1칸 이동할때마다 정수의 값은 반이 된다는 것을 알 수 있다.

이는 상황에 따라 곱셈과 나눗셈을 비트의 이동 연산으로 구할 수 있다는 것을 나타내고, CPU 입장에서는 곱셈과 나눗셈이 비트 이동보다 부담스러운 연산이기 때문에 성능 향상으로 이어질 수 있다.

비트 플래그

메모리의 최소 크기 단위는 1 byte 이므로 변수는 적어도 1 byte 이상의 크기를 가진다.

이때 1 byte 는 8 bit 이므로 8가지의 상태를 저장할 수 있다. 반면 1 byte 크기를 가지는 bool 자료형 은 8 bit 중 1 bit 만을 사용하고 7 bit 를 낭비하면서 1가지 상태만을 저장한다. 따라서 비트 플래그(Bit Flag)를 이용하면 bool 자료형보다 훨씬 효율적으로 상태를 저장할 수 있다.