지난 포스팅에서 첫 프로그래밍을 해보았습니다. 출력하고 싶은 메세지를 마음껏 출력하고 입력받고 싶은 메세지를 여러가지 방법으로 입력받아 보셨나요? 기본적인 내용이지만 꼭 필요한 내용이기도 합니다. 저의 경우는 타자로 치면서 익숙해지도록 만드는 편입니다.
이번 포스팅에서는 연산자라는 것을 알아볼 예정입니다. 연산자는 이름 그대로 연산을 할때 사용할 요소들을 말합니다. 이러한 연산들에는 다음과 같은 것들이 있습니다.
- 산술 연산자 Arithmetic Operators
- 증감 연산자 Increment and Decrement Operators
- 대입 연산자 Assignment Operators
- 관계 연산자 Relational Operators
- 논리 연산자 Logical Operators
- 비트 연산자 Bitwise Operators
- 그 밖에 연산자
연산자의 종류도 많고 생각보다 이름도 어렵네요. 막상 수식을 보거나 사용법을 보면 어렵지는 않습니다. 그럼 하나씩 다뤄보도록 하겠습니다.
산술 연산자 Arithmetic Operators
산술 연산자는 사칙연산에 사용하는 기호들을 의미합니다. 더하기(+), 빼기(-), 곱하기(*), 나누기(/)가 있습니다.
각각의 이름은 다음과 같이 부릅니다.
연산자 | 연산자 의미 |
+ | 덧셈 연산자 |
- | 뺄셈 연산자 |
* | 곱셈 연산자 |
/ | 나눗셈 연산자 |
% | 나눗셈 결과의 나머지를 나타내는 연산자 |
소스코드1 arithmeticOper.c
#include <stdio.h>
int main(int argc, const char* argv[]) {
int num1 = 10;
int num2 = 3;
int ret;
ret = num1 + num2; // 덧셈 연산자
printf("%d + %d = %d\n", num1, num2, ret);
ret = num1 - num2; // 뺌셈 연산자
printf("%d - %d = %d\n", num1, num2, ret);
ret = num1 * num2; // 곱셈 연산자
printf("%d * %d = %d\n", num1, num2, ret);
ret = num1 / num2; // 나눗셈 연산자
printf("%d / %d = %d\n", num1, num2, ret);
ret = num1 % num2; // 나머지 연산자
printf("%d %% %d = %d\n", num1, num2, ret);
return 0;
}
결과1
10 + 3 = 13
10 - 3 = 7
10 * 3 = 30
10 / 3 = 3
10 % 3 = 1
산술연산자의 결괏값을 알아보도록 하겠습니다.덧셈, 뺄셈, 곱셈, 나눗셈, 나머지 연사자들을 각각 사용해서 결괏값을 출력했습니다. 두 수는 num1 과 num2 변수에 각각 10과 3을 저장했고 결과는 ret 변수에 담도록 했습니다. 그리고 모든 변수는 정수형 데이터 타입인 int 로 선언했습니다.
덧셈(+), 뺄셈(-), 곱셈(*) 연산자의 경우는 우리가 아는 숫자 그대로 결과를 저장하고 출력합니다. 눈여겨 보아야 할 부분은 나눗셈과 나머지 연산자입니다.
10 / 3 = 3.33.. 입니다. 하지만 결과는 3만 출력됩니다. 이유는 integer 데이터 타입으로 선언하였기 때문에 소수점 부분은 버려진 3만 저장되어 출력하게 되는 것입니다.
나머지 연산자(%) 는 나눗셈을 한 나머지 결괏값을 반영합니다. 10/3은 몫이 3이고 나머지가 1입니다. 따라서 결괏값은 1이 되는 것입니다. 추가적으로 printf() 함수를 사용해서 %를 출력하기 위해서는 %% 로 사용하면 됩니다.
증감 연산자 Increment and Decrement Operators
증감 연산자는 의미 그대로 증가와 감소를 표현할때 사용하는 연산자 입니다.이때 증감 연산자는 변수에 저장된 값에 1을 증거하거나 1을 감소시키는 역할을 하는 연산자들입니다. 이때 연산자는 ++, -- 연산자로 표현합니다.
연산자 | 연산자 의미 |
++ | 증가 연산자 |
-- | 감소 연산자 |
소스코드2 incdecOper.c
#include <stdio.h>
int main(int argc, const char* argv[]) {
int num1 = 10;
int num2 = 5;
printf("num1++ = %d\n", num1++); // 1.
printf("++num1 = %d\n", ++num1); // 2.
printf("num2-- = %d\n", num2--); // 3.
printf("--num2 = %d\n", --num2); // 4.
printf("num1: %d, num2: %d\n", num1, num2);
return 0;
}
결과2
num1++ = 10
++num1 = 12
num2-- = 5
--num2 = 3
num1: 12, num2: 3
증가연산자(++)와 감소연산자(--) 는 변수의 앞과 변수의 뒤에 사용함에 따라서 결괏값이 달라지게 됩니다.
변수의 앞에 붙을때에는 현재 라인에서는 증가하지 않고 다음라인으로 넘어갈때 증가 또는 감소 계산을 합니다.
변수의 뒤에 붙을때에는 현재 라인에서 계산을 먼저하고 다음 라인으로 넘어갑니다. 위에 결과로 다시한번 설명드리겠습니다.
1. 변수 num1 의 초기값은 10입니다. 현재 라인에서 num1++ 증가 연산자를 사용했으므로 현재 라인에서는 num1 의 현재값인 10을 출력하고 다음 라인에서는 1 증가하여 11을 가지고 있게 됩니다.
2. 변수 num1 은 현재 11이고 ++num1 증가 연산자를 사용했으므로 먼저 1을 증가시켜야 합니다. 따라서 12가 되어 출력값 12를 출력하고 다음라인으로 넘어갑니다.
3. 변수 num2는 현재 5입니다. 현재 라인에서 num2-- 감소 연산자를 사용했으므로 현재 라인에서는 5를 출력하고 다음 라인으로 넘어가면 1 감소하여 4를 가지고 있게 됩니다.
4. 변수 num2 의 현재값은 4입니다. 현재 라인에서 --num2 감소 연산자를 사용했으므로 먼저 1을 감소시켜야 합니다. 따라서 3을 출력하고 다음 라인으로 넘어갑니다.
마지막 결괏값은 num1은 12, num2는 3이 되는 것입니다.
대입 연산자 Assignment Operators
대입 연산자는 사칙연산의 결과를 변수에 바로 대입하여 저장하는 연산자를 의미합니다.
산술연산자의 결과를 바로 저장하기 때문에 산술연산자(+, -, *, /, %)와 등호(=)를 결합하여 연산자를 만든 형태입니다.
대입 연산자 | 연산자 사용법 | 연산자 의미 |
= | a = b | 변수 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 |
소크코드3 assignOper.c
#include <stdio.h>
int main(int argc, const char* argv[]) {
int num1 = 10;
int num2;
num2 = num1;
printf("num2 = %d\n", num2); // 1.
num2 += num1;
printf("num2 = %d\n", num2); // 2.
num2 -= num1;
printf("num2 = %d\n", num2); // 3.
num2 *= num1;
printf("num2 = %d\n", num2); // 4.
num2 /= num1;
printf("num2 = %d\n", num2); // 5.
num2 %= num1;
printf("num2 = %d\n", num2); // 6.
printf("num1 = %d\n", num1); // 7.
return 0;
}
결과3
num2 = 10
num2 = 20
num2 = 10
num2 = 100
num2 = 10
num2 = 0
num1 = 10
대입연산자 프로그램의 결과를 함께 보도록 하겠습니다. 우선 모두 정수형 데이터 타입인 int 를 사용하여 변수 num1 과 num2 를 선언하고 num1 은 10으로 초기화 하였습니다.
1. 변수 num1 이 가지고 있는 값을 num2 에 대입한 것이므로 num2 의 값은 10입니다.
2. 변수 num2 가 가지고 있는 값에 num1 을 더하여 다시 num2 에 대입하는 것이므로 10 + 10 = 20 이 됩니다.
3. 변수 num2 가 가지고 있는 값에 num1 을 빼고 다시 num2 에 대입하는 것이므로 20 - 10 = 10 이 됩니다.
4. 변수 num2 가 가지고 있는 값에 num1 을 곱하고 다시 num2 에 대입하는 것이므로 10 * 10 = 100 이 됩니다.
5. 변수 num2 가 가지고 있는 값에 num1 을 나눈 값의 정수부분을 num2 에 대입하는 것이므로 100 / 10 = 10 입니다.
6. 변수 num2 가 가지고 있는 값에 num1 을 나눈 나머지의 정부수분을 num2 에 대입하는 것이므로 10 % 10 = 0 입니다.
7. 변수 num1 은 계산 결과에 반영되지 않는 변수이므로 num1 은 10 입니다.
관계 연산자 Relational Operators
관계 연산자는 조건문에도 많이 사용하는 연사자입니다. 두 변수의 관계를 비교하고 그 결과를 참(True) 과 거짓(Flase) 로 반환합니다. 참일 경우는 1 이며 거짓일 경우는 0 입니다. 관계 연산자의 종류는 다음과 같습니다.
관계 연산자 | 사용법 | 의미 | 결과 |
== | 3 == 2 | 3 과 2 가 같은가? | 0 |
>= | 3 >= 2 | 3 은 2 보다 크거나 같은가? | 1 |
<= | 3 <= 2 | 3 은 2 보다 작거나 같은가? | 0 |
!= | 3 != 2 | 3 은 1 과 같지 않은가? | 1 |
> | 3 > 2 | 3 은 2 보다 큰가? | 1 |
< | 3 < 2 | 3 은 2 보다 작은가? | 0 |
소스코드4 relationalOper.c
#include <stdio.h>
int main(int argc, const char* argv[]) {
int num1 = 3;
int num2 = 2;
printf("%d == %d ? %d\n", num1, num2, num1 == num2); // 1.
printf("%d >= %d ? %d\n", num1, num2, num1 >= num2); // 2.
printf("%d <= %d ? %d\n", num1, num2, num1 <= num2); // 3.
printf("%d != %d ? %d\n", num1, num2, num1 != num2); // 4.
printf("%d > %d ? %d\n", num1, num2, num1 > num2); // 5.
printf("%d < %d ? %d\n", num1, num2, num1 < num2); // 6.
return 0;
}
결과4
3 == 2 ? 0
3 >= 2 ? 1
3 <= 2 ? 0
3 != 2 ? 1
3 > 2 ? 1
3 < 2 ? 0
관계 연산자 프로그래밍의 결과를 함께 보도록 하겠습니다.
1. 3과 2의 관계연산자 비교는 == 입니다. 둘은 같지 않으므로 결과는 0입니다.
2. 3은 2보다 크거나 같은지 비교하는 관계연산자(>=) 입니다. 결과는 1입니다.
3. 3은 2보다 작거나 같은지 비교하는 관계연산자(<=) 입니다. 결과는 0입니다.
4. 3은 2과 같지 않음을 비교하는 관계연산자(!=) 입니다. 결과는 1입니다.
5. 3은 2보다 큰지 비교하는 관계연산자는(>) 입니다. 결과는 1입니다.
6. 3은 2보다 작은지 비교하는 관계연산자는(<) 입니다. 결과는 0입니다.
논리 연산자 Logical Operators
논리 연산자는 보통 Bit 비교에서 많이 사용하는 연산자입니다.
논리회로를 들어보신분들이라면 쉽게 이해하실수 있지만 모르셔도 이해하는데에는 어렵지 않습니다. 논리 연산자에는 3가지 종류가 있습니다. AND, OR, NOT 입니다. 각각은 다음과 같은 결과를 가집니다.
AND 연산자
좌항 | 우항 | 결과 |
0 | 0 | 0 |
0 | 1 | 0 |
1 | 0 | 0 |
1 | 1 | 1 |
AND 연산자는 좌항과 우항이 모두 1일때만 결과로 1의 연산결과가 나옵니다. 0은 False 를 뜻하고 1은 True 를 뜻합니다. 따라서 양쪽의 결과를 논리 연산자로 비교할 수 있습니다. AND 연산자는 && 로 표현합니다.
OR 연산자
좌항 | 우항 | 결과 |
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 1 |
OR 연산자는 좌항 또는 우항 둘 중 하나라도 1일면 결과는 1이 나옵니다. 비교하는 항들 중에서 True 가 있다면 무조건 True 가 되는 연산이 OR 연산자 입니다. OR 연산자는 || 로 표현합니다.
NOT 연산자
항 | 결과 |
0 | 1 |
1 | 0 |
NOT 연산자는 항의 반대 결과를 냅니다. 청개구리와 같은 연산자라고 할 수 있겠네요. NOT 연산자는 ! 로 표현합니다.
소스코드5 logicalOper.c
#include <stdio.h>
int main(int argc, char* argv[]) {
int num1 = 3;
int num2 = 1;
int num3 = 2;
int ret;
ret = (num1 >= num2) && (num1 >= num3); // 1.
printf("ret = %d\n", ret);
ret = (num1 >= num2) && (num1 < num3); // 2.
printf("ret = %d\n", ret);
ret = (num1 >= num2) || (num1 == num3); // 3.
printf("ret = %d\n", ret);
ret = (num1 < num2) || (num1 == num3); // 4.
printf("ret = %d\n", ret);
ret = !(num1 == num3); // 5.
printf("ret = %d\n", ret);
return 0;
}
결과5
ret = 1
ret = 0
ret = 1
ret = 0
ret = 1
논리 연산자 프로그래밍 소스코드를 보겠습니다. 변수는 4개입니다. 모두 int 데이터 타입의 변수이며 결과를 대입하는 ret 을 제외하고 각각 3, 1, 2 로 초기화 했습니다.
1. 변수 num1 >= num2 의 결과는 3 >= 1 이므로 1입니다. 변수 num1 >= num3 의 결과는 3 >= 2 이므로 1입니다. 따라서 1 && 1 의 결괏값은 1 입니다. AND 연산자로 둘다 1이면 이 결과는 1이기 때문입니다.
2. 변수 num1 >= num2 의 결과는 3 >= 1 이므로 1입니다. 변수 num1 < num3 의 결과는 3 < 2 이므로 0입니다. 따라서 1 && 0 의 결과는 0입니다.
3. 변수 num1 >= num2 의 결과는 3 >= 1 이므로 1입니다. 변수 num1 == num3 의 결과는 3 == 2 이므로 0입니다. 따라서 1 || 0 의 결과는 1입니다. OR 연산자로 둘중 하나라도 1 이면 결과는 1입니다.
4. 변수 num1 < num2 의 결과는 3 < 1 이므로 0입니다. 변수 num1 == num3 의 결과는 3 == 2 이므로 0입니다. 따라서 0 || 0 의 결과는 0입니다.
5. 변수 num1 == num3 의 결과는 3 == 2 이므로 0입니다. 따라서 !0 의 결과는 1입니다. NOT 연산자로 0의 반대인 1입니다.
비트 연산자 Bitwise Operators
비트 연산자는 2진수의 진수변화를 이해하면 조금 더 쉽게 이해하실수 있습니다.
비트로 변경한 값에 대해서 논리 연산을 수행합니다. 진수변환에 대해서는 다음 포스팅에서 알아보도록 하고 비트 연산자에는 아래와 같은 종류들이 있습니다.
비트 연산자 | 의미 |
& | AND |
| | OR |
^ | XOR |
~ | NOT |
<< | Left Shift |
>> | Right Shift |
비트 연산자 & 는 AND 연산자를 의미합니다. 따라서 논리연산에서 보았듯 두 수가 모두 1일 때 1일 출력합니다.
비트 연산자 | 는 OR 연산자를 의미합니다. 따라서 논리연산에서 보았듯 두 수중에서 하나의 수라도 1일때 1일 출력합니다.
비트 연산자 ^는 XOR 연산자입니다. XOR 는 eXclusive OR 라는 의미로 배타적 OR 라는 의미입니다. 이 경우는 OR와 기능이 동일하지만 두 수가 같은 경우는 0을 출력하고, 다른 경우만 1을 출력합니다.
비트 연산자 ~는 NOT 연산자이며 0일때 1을, 1일때 0을 출력하는 연산자입니다.
비트 연산자 <<는 Left Shift 연산자입니다. 왼쪽으로 모두 n bit 이동하라는 의미입니다. 예를들어서 숫자 3을 2진수로 표현하면 0011 입니다. 이때 왼쪽으로 1bit 이동하면 0110 이 됩니다. 이 결과는 6입니다.
비트 연산사 >>는 Right Shift 연산자입니다. 오른쪽으로 모두 n bit 이동하라는 의미입니다. 위와 동일하게 숫자 3을 2진수로 표현하면 0011 입니다. 이때 오른쪽으로 1bit 이동하면 0001 입니다. 따라서 결과는 1입니다.
소스코드6 bitwiseOper.c
#include <stdio.h>
int main(int argc, char* argv[]) {
int num1 = 5; // 0000 0101
int num2 = 3; // 0000 0011
int ret;
ret = num1 & num2; // 1.
printf("%d\n", ret);
ret = num1 | num2; // 2.
printf("%d\n", ret);
ret = num1 ^ num2; // 3.
printf("%d\n", ret);
ret = ~num1; // 4.
printf("%d\n", ret);
ret = num2 << 1; // 5.
printf("%d\n", ret);
ret = num1 >> 1; // 6.
printf("%d\n", ret);
return 0;
}
결과6
1
7
6
-6
6
2
비트 연산자는 모두 2진수로 계산하기 때문에 간단한 수로 예를 들어서 진행하도록 하겠습니다. 우선 정수형 데이터 타입인 int 자료형으로 변수 num1, num2, ret 을 선언하고 num1과 num2 는 각각 5와 3으로 초기화 했습니다.
1. 변수 num1 & num2 는 5 & 3 입니다. 2진수로 변환하면 0101 & 0011 입니다. 각 자리수에 맞는 수끼리 비트연산을 진행하시면 됩니다.
0101
0011
===
0001
따라서 결과는 0001 이고 10진수로 변환하면 1입니다.
2. 변수 num1 | num2 는 5 | 3 입니다. 2진수로 변환하면 0101 | 0011 이고 비트연산을 해보겠습니다.
0101
0011
===
0111
따라서 결과는 0111 이고 10진수로 변환하면 7입니다.
3. 변수 num1 ^ num2 는 5 ^ 3 입니다. 비트연산을 바로 하겠습니다.
0101
0011
===
0110
따라서 결과는 0110 이고 10진수로 변환하면 6입니다. XOR 는 OR 연산에 서로 같은수는 0이 되는 연산입니다.
4. 계산은 ~num3 의 값을 num4 에 대입하는 것입니다. num3 값은 5입니다. 따라서 0101 입니다. 여기서 NOT 연산을 진행하면 1010 이 됩니다. 그런데, 이것을 10진수로 표현하기위해서는 좀 다릅니다. 이때에는 1의 보수와 2의 보수 개념이 필요합니다.
우리는 간단하게 5를 0101 이라 표현했지만 32bit 표현방법을 사용하면 0000 0000 0000 0000 0000 0000 0000 0101 입니다. 여기서 NOT 연산을 하게되면 1111 1111 1111 1111 1111 1111 1111 1010 입니다. 이때 중요한것은 가장 왼쪽에 위치한 값이 부호를 뜻하는 bit 이며 음수라는 것을 의미합니다. 이 수를 10진수로 나타내기 위해서는 2의보수로 표현해야 합니다.
먼저 전체 비트를 반대로 바꾸고 결과에 1을 더하면 됩니다. 전체를 반대로 바꾸면 부호를 제외하고 ... 0000 0101 이 되죠. 여기에 1을 더하면 ... 0000 0110 이 됩니다. 이는 6이고 부호는 (-) 입니다. 따라서 결과는 -6이 되는 것입니다.
5. 변수 num2 << 1 의 값은 3 << 1 입니다. 2진수로 변환하면 0011 << 1 이고 왼쪽으로 1비트 이동하기 때문에 0110이 됩니다. 따라서 결과는 6입니다.
6. 변수 num1 >> 1 의 값은 5 >> 1 입니다. 2진수로 변환하면 0101 >> 1 이고 오른쪽으로 1비트 이동하기 때문에 0010이 됩니다. 따라서 결과는 2입니다.
비트 연산자는 2진수와 10진수 진수변환이 익숙해져야 더 쉽게 이해하실수 있습니다. (계산기를 사용하시면 프로그래머용으로 켜실수 있고 16진수, 10진수, 8진수, 2진수 모두 확인이 더 쉽게 가능합니다.)
그 밖에 연산자
앞서 나온 연산자 외에 사용되는 연산자들은 *, &, [], ., -> 등이 있습니다. 이들은 구조체, 포인터, 배열에서 모두 사용되기 때문에 이후 자세하게 알아보도록 하겠습니다.
오늘은 포스팅이 매우 길었네요. 연산자의 종류가 많고 소스코드 하나씩 보다보니 포스팅이 길어진것 같습니다. 반복문이나 조건문에서 연산자의 쓰임이 굉장히 많고, 비트 연산자는 암호화 프로그래밍을 할 때에 자주 사용됩니다. (특히 에러코드 정의할때 많이 사용합니다.)
지금까지 C언어에서 사용하는 연산자였습니다.
'쿤즈 Dev > C' 카테고리의 다른 글
[C언어] 조건문을 사용해서 분기시켜보자(2) - switch case 조건문! (0) | 2020.08.14 |
---|---|
[C언어] 조건문을 사용해서 분기시켜보자(1) - if 조건문! (0) | 2020.08.10 |
[C언어] 표준입력 표준출력 사용하기 (첫 프로그래밍 시작!) (2) | 2020.08.06 |
[C언어] 데이터 타입(Data Types) 과 함께 변수 선언하기 (0) | 2020.08.04 |
[C언어] 변수 (Variables) vs 상수 (Constraints) (0) | 2020.08.02 |
댓글