◎ 연산자(Operator)
연산자란 말 그대로 연산을 수행하는 기호를 의미하고, 피연산자(Operand)는 연산에 참여하는 변수나 상수 값을 의미한다. 연산자의 종류는 아래 표와 같다.
분류 | 연산자 |
대입 연산자 | = |
산술 연산자 | +, -, *, /, % |
복합 대입 연산자 | +=, -=, *=, /=, %= |
증감 연산자 | ++, -- |
관계 연산자 | >, <, ==, !=, >=, <= |
논리 연산자 | &&, ||, ! |
조건 연산자(삼항 연산자) | ? : |
비트 논리 연산자 | &, |, ^, ~ |
비트 이동 연산자 | >>, << |
1) 대입 연산자
대입 연산자는 i = 3과 같이 오른쪽에 있는 값을 왼쪽의 피연산자에 대입하는 연산을 수행하기 위해 사용한다.
#include <stdio.h>
int main() {
int a = 5;
int b = 3;
int c = 2;
// 이처럼 변수에 값을 대입할 때 사용
}
2) 산술 연산자
산술 연산자는 덧셈을 수행하는 +, 뺄셈을 수행하는 -, 곱셈을 수행하는 *, 나눗셈을 수행하는 /, 나머지 연산을 수행하는 %이 있다. 추후 코딩을 할 때 나머지 연산자를 잘 활용하면 효율적인 알고리즘을 구성할 수 있다.
#include <stdio.h>
int main() {
int x = 6, y = 3;
printf("덧셈 : %d \n", x + y);
printf("뺄셈 : %d \n", x - y);
printf("곱셈 : %d \n", x * y);
printf("나눗셈 : %d \n", x / y);
printf("나머지 : %d \n", x % y);
return 0;
}
나머지 연산자가 활용될 수 있는 간단한 예시를 들어보면 1234라는 숫자의 각 자릿수를 따로따로 분리하여 얻는 것이 있다.
#include <stdio.h>
int main() {
int x = 1234;
printf("1000의 자리 : %d \n", x/1000);
printf("100의 자리 : %d \n", (x%1000) / 100);
printf("10의 자리 : %d \n", (x%100) / 10);
printf("1의 자리 : %d \n", (x%10) / 1);
return 0;
}
3) 복합 대입 연산자
복합 대입 연산자는 산술 연산자와 대입 연산자를 하나로 나타내는 연산을 수행한다. 그 형태는 산술 연산자 뒤에 등호(=)가 결합된 것이다. 산술 후 대입하는 과정의 번거로움을 줄임으로써 코드를 간결하게 작성할 수 있다.
#include <stdio.h>
int main() {
int a = 6, b = 3;
a += b; // a = a + b;
printf("a : %d, b : %d \n", a, b);
a -= b; // a = a - b;
printf("a : %d, b : %d \n", a, b);
a *= b; // a = a * b;
printf("a : %d, b : %d \n", a, b);
a /= b; // a = a / b;
printf("a : %d, b : %d \n", a, b);
a %= b; // a = a % b;
printf("a : %d, b : %d \n", a, b);
return 0;
}
4) 증감 연산자
증감 연산자는 피연산자의 값을 1 증가 또는 1 감소 하기 위해 사용하는 연산자다. 종류는 ++와 --가 있는데 숫자 앞에 사용하는 경우와 뒤에 사용하는 경우에 따라 연산의 결과가 달라질 수 있음에 주의해야 한다. 숫자 앞에 연산자를 사용하게 되면 우선 1 증가 또는 1 감소를 진행하고 다음 연산을 수행한다. 그러나 숫자 뒤에 연산자를 사용하게 되면 다른 연산을 다 수행하고 가장 마지막에 1 증가 또는 1 감소를 수행하게 된다.
#include <stdio.h>
int main() {
int a = 10;
printf("++a : %d \n", ++a); // a = a + 1 = 11
printf("a++ : %d \n", a++); // a = a + 1 = 12
printf("연산 후 a의 값 : %d \n\n", a);
printf("--a : %d \n", --a); // a = a - 1 = 11
printf("a-- : %d \n", a--); // a = a - 1 = 10
printf("연산 후 a의 값 : %d \n\n", a);
return 0;
}
위 결과를 확인해보면 a++과 a--는 연산이 이뤄지기 전의 값이 출력되고 있는 것을 알 수 있다.
5) 관계 연산자(비교 연산자)
관계 연산자는 특정 두 수의 대소 관계를 확인하기 위해 사용하거나 조건문에서 참, 거짓을 확인하기 위해 사용한다. 대부분의 관계 연산자는 수학에서 이미 보았지만 같은지 같지 않은지를 확인하기 위한 연산자 ==, !=는 수학에서 보지 못한 연산자들이다. 앞서 이미 대입 연산자에서 등호를 한개(=) 사용했으므로 비교 연산자에서는 등호를 두개(==) 사용하는 것이다.
관계 연산자의 결과는 참, 거짓인데 C언어에서 이는 정수 1과 0으로 나타내고 있다. 결과가 참이라면 1, 거짓이라면 0을 반환한다.
#include <stdio.h>
int main() {
int a = 3, b = 3;
printf("a>b : %d \n", a>b);
printf("a<b : %d \n", a<b);
printf("a>=b : %d \n", a>=b);
printf("a<=b : %d \n", a<=b);
printf("a==b : %d \n", a==b);
printf("a!=b : %d \n", a!=b);
return 0;
}
6) 논리 연산자
논리 연산자의 종류로는 and 연산자 &&, or 연산자 ||, not 연산자 !이 있다. 앞서 결과가 참이라면 1을, 거짓이라면 0을 반환한다 하였는데 사실 C언어에서 0을 제외한 모든 숫자는 참으로 간주된다.
6-1) and 연산자 &&
and 연산자 &&는 피연산자가 모두 참이어야 참을 반환한다.
#include <stdio.h>
int main() {
printf("0 && 0 : %d \n", 0 && 0);
printf("0 && 1 : %d \n", 0 && 1);
printf("1 && 0 : %d \n", 1 && 0);
printf("1 && 1 : %d \n", 1 && 1);
return 0;
}
6-2) or 연산자 ||
or 연산자 ||는 피연산자 중 하나만 참이어도 참을 반환한다.
#include <stdio.h>
int main() {
printf("0 || 0 : %d \n", 0 || 0);
printf("0 || 1 : %d \n", 0 || 1);
printf("1 || 0 : %d \n", 1 || 0);
printf("1 || 1 : %d \n", 1 || 1);
return 0;
}
6-3) not 연산자 !
not 연산자 !는 참을 거짓으로, 거짓을 참으로 반환한다.
#include <stdio.h>
int main() {
printf("!1 : %d \n", !1);
printf("!0 : %d \n", !0);
return 0;
}
6-4) 0이면 거짓, 0이 아니면 참
앞서 언급한 것처럼 C언어에서는 0이면 거짓이고 0이 아니면 모두 참으로 간주된다. 이를 논리 연산자를 통해 확인해보자.
#include <stdio.h>
int main() {
printf("123 && -10 : %d \n", 123 && -10);
printf("0.123 && 39485739 : %d \n", 0.123 && 39485739);
return 0;
}
7) 조건 연산자(삼항 연산자)
조건 연산자는 피연산자가 3개 필요함에 따라 삼항 연산자라고도 부른다. 그 형태는 다음과 같다.
(조건문) ? 식1 : 식2
조건문이 참이라면 식1을 실행하고, 거짓이라면 식2를 실행한다. 이를 사용하면 if문보다 간결한 표현이 가능하다.
#include <stdio.h>
int main() {
(10 > 5) ? printf("10은 5보다 큽니다. \n") : printf("10은 5보다 작습니다. \n");
return 0;
}
8) 비트 논리 연산자
컴퓨터에서는 우리가 일상에서 사용하는 10진수와 2진수, 16진수, 8진수 표현법을 사용할 수 있다. 10진수를 2진수, 16진수, 8진수로 변환하는 방법은 생략하도록 한다. 컴퓨터는 모든 정보를 0과 1의 2진수로 처리한다. 2진수에서 0, 1과 같이 신호를 나타내는 최소 단위를 비트(bit)라고 한다. 1bit는 2가지 값(0, 1)을 가질 수 있고, 2bit는 4가지 값(00, 01, 10, 11)을 가질 수 있으며, 3bit는 8가지 값(000, 001, 010, 100, 011, 101, 110, 111)을 가질 수 있다. 이처럼 n개의 비트는 2^n개의 값을 가질 수 있다.
비트 논리 연산자는 피연산자를 2진수로 변환했을 때의 값을 기준으로 연산을 수행한다. 비트 논리 연산자의 종류는 아래 표와 같다.
비트 연산자 | 연산식 | 설명 |
& | a & b | 비트 단위 AND 연산 |
| | a | b | 비트 단위 OR 연산 |
^ | a ^ b | 비트 단위 XOR 연산 |
~ | ~a | 비트 단위 NOT 연산 |
8-1) AND 연산 &
& 연산자는 두 비트가 모두 1로 같을 때만 1을 반환한다. 10진수 20과 16은 2진수로 각각 0001 0100(2), 0001 0000(2)다. 이 두 수를 AND 연산 하면 다섯 번째 비트만 1로 같으므로 결과는 0001 0000(2)가 되고 이는 10진수로 16이다.
#include <stdio.h>
int main() {
// 20 : 0001 0100(2)
// 16 : 0001 0000(2)
// & : 0001 0000(2) => 16(10)
printf("20 & 16 : %d \n", 20 & 16);
return 0;
}
8-2) OR 연산 |
| 연산자는 두 비트 중 하나만 1이더라도 1을 반환한다. 이번에도 20과 16을 사용해보도록 하겠다.
#include <stdio.h>
int main() {
// 20 : 0001 0100(2)
// 16 : 0001 0000(2)
// | : 0001 0100(2) => 20(10)
printf("20 | 16 : %d \n", 20 | 16);
return 0;
}
8-3) XOR 연산 ^
^ 연산자는 두 비트가 서로 달라야만 1을 반환한다. 즉, 하나는 0이고 다른 하나는 1일 때만 1을 반환한다는 것이다.
#include <stdio.h>
int main() {
printf("0 ^ 0 : %d \n", 0 ^ 0);
printf("0 ^ 1 : %d \n", 0 ^ 1);
printf("1 ^ 0 : %d \n", 1 ^ 0);
printf("1 ^ 1 : %d \n", 1 ^ 1);
return 0;
}
8-4) NOT 연산 ~
~ 연산자는 비트를 반전시킨다. 0이라면 1로, 1이라면 0으로 반전시켜 반환한다.
#include <stdio.h>
int main() {
printf("~0 : %d \n", ~0); // 0000 0000(2) -> 1111 1111(2) == -1(10)
printf("~1 : %d \n\n", ~1); // 0000 0001(2) -> 1111 1110(2) == -2(10)
printf("~20 : %d \n", ~20); // 0001 0100(2) -> 1110 1011(2) == -21(10)
printf("~16 : %d \n", ~16); // 0001 0000(2) -> 1110 1111(2) == -17(10)
return 0;
}
~ 연산자를 이해하기 위해서는 2의 보수에 대해 알아야 한다. 이는 대부분의 컴퓨터 시스템에서 음수의 표현을 위해 사용하는 방법인데 간단히 말하자면 2진수로 나타낸 수에서 0이면 1로, 1이면 0으로 바꾸고(이를 1의 보수를 취한다고 함), 그 값에 1을 더하는 것이다. 10진수 20을 ~연산 한 값은 1110 1011(2)인데 이것이 10진수로 어떤 값에 해당하는지를 확인하기 위해서는 우선 0과 1을 반전시키고(== 1의 보수를 취하고) 거기에 1을 더하면 된다. 즉, 0001 0101(2)가 되는데 이는 21이므로 1110 1011(2)는 -21이라는 것을 알 수 있는 것이다.
★9) 비트 이동 연산자
비트 이동 연산자는 비트를 왼쪽으로 이동시키는 <<와 오른쪽으로 이동시키는 >>가 있다. 예를 들어 20<<1은 10진수 20을 2진수로 나타낸 값 0001 0100(2)에서 모든 비트를 왼쪽으로 한 칸 이동시키는 연산을 수행하는데 그럼 결과는 0010 1000(2)가 된다. << 연산을 수행했을 때 생기는 맨 오른쪽 빈자리 비트는 수가 양수일 경우에는 0으로 채워지고 음수일 경우에는 1로 채워진다.
비트를 오른쪽으로 이동시키는 >> 연산은 정확히 << 연산과 반대이다. 마찬가지로 10진수 20을 사용하여 20>>1 연산을 수행해보자. 20(10)은 0001 0100(2)인데 이를 오른쪽으로 한 칸 이동하게 되면 0000 1010(2)가 된다. >> 연산을 수행했을 때 생기는 맨 왼쪽 빈자리 비트는 수가 양수일 경우에는 0으로 채워지고 음수일 경우에는 1로 채워진다.
#include <stdio.h>
int main() {
// 10진수 20은 2진수로 0001 0100(2)
printf("20 << 1 : %d \n", 20 << 1); // 0001 0100(2) -> 0010 1000(2)
printf("20 << 2 : %d \n", 20 << 2); // 0001 0100(2) -> 0101 0000(2)
printf("20 << 3 : %d \n\n", 20 << 3); // 0001 0100(2) -> 1010 0000(2)
printf("20 >> 1 : %d \n", 20 >> 1); // 0001 0100(2) -> 0000 1010(2)
printf("20 >> 2 : %d \n", 20 >> 2); // 0001 0100(2) -> 0000 0101(2)
printf("20 >> 3 : %d \n", 20 >> 3); // 0001 0100(2) -> 0000 0010(2)
return 0;
}
★ ★ 위 결과를 살펴보면 한 가지 특징을 발견할 수 있다. 왼쪽으로 1칸씩 이동할 때마다 값이 2배가 되고 반대로 오른쪽으로 1칸씩 이동할 때마다 값이 1/2배가 된다. 비트 이동 연산자는 2진수를 왼쪽 또는 오른쪽으로 이동시키는 것이므로 1번 이동할 때마다 2배 또는 1/2배만큼의 값 변화가 일어나게 되는 것이다. 비트 이동 연산자를 잘 활용하면 알고리즘을 간단하게 구현하는 것이 가능할 때가 있다.
'[Programming Language] > [C언어]' 카테고리의 다른 글
[C언어] :: %d와 %i의 차이점 (0) | 2024.02.12 |
---|---|
[C언어] :: 상수(리터럴 상수, 심볼릭 상수, const, define) (0) | 2024.01.26 |
[C언어] :: 변수, 변수 선언 방법 및 주의사항, 변수의 사용, 변수의 시작 주소 및 & 연산자 (0) | 2024.01.25 |
[C언어] :: 기본 입출력(printf, 특수 문자, 서식 문자, scanf) (0) | 2024.01.18 |
[C언어] :: C언어 기본 구조(주석, 전처리기, 헤더파일, main 함수, 세미콜론, return) (1) | 2024.01.16 |