쉽게 쓰여진 C

[C언어 4강] 연산자 + 이진수 체계

이런우 2023. 4. 29. 20:58

이번엔 계산을 하기 위한 연산자를 알아볼 것이다.
 

연산자 : 프로그래밍 언어에서 일반적으로 연산의 집합을 의미한다.

 
프로그래밍에서 사용되는 연산자의 종류는 다음과 같다.
 

  • 대입 연산자 : =
  • 산술 연산자 : + , - , * ,  / ,  %
  • 논리 연산자 : ! , && , ||
  • 증감 연산자 :  ++ , --
  • 삼항연산자 : ?  :
  • 비트 연산자 : ! , ~ , & , ^ , >> , <<

먼저 우선순위  가장 먼저 처리되는 순서를 대충이라도 확인하고 넘어가자
 

기호 연산 유형associativity
[ ] ( ) . ->++--(후위)왼쪽에서 오른쪽
sizeof & * + - ~ !++--(전위)단항오른쪽에서 왼쪽
형식 캐스팅단항오른쪽에서 왼쪽
* / %곱하기왼쪽에서 오른쪽
+ -더하기왼쪽에서 오른쪽
<< >>비트 시프트왼쪽에서 오른쪽
< > <= >=관계왼쪽에서 오른쪽
== !=같음왼쪽에서 오른쪽
&비트 AND왼쪽에서 오른쪽
^비트 제외 OR왼쪽에서 오른쪽
|비트 포함 OR왼쪽에서 오른쪽
&&논리 AND왼쪽에서 오른쪽
||논리 OR왼쪽에서 오른쪽
? :조건식오른쪽에서 왼쪽
= *= /= %=+= -= <<= >>= &=^= |=단순 및 복합 할당 2오른쪽에서 왼쪽
,순차적 계산왼쪽에서 오른쪽

 
지금은 굳이 외울 필요 없으니 대충 보고 패스!
 

대입 연산자

 
간단하게 생각해서 뭐뭐 = 뭐뭐 다. 할 때 쓰는 = 이다.
 
=int a = 1; 이런 식으로 보통 왼쪽 변수에 오른쪽의 값을 할당할 때 사용한다.
 
이것은 우리가 이미 써봤으니 여기까지 설명하기로 하겠다.
 
(****추가****)
 
= , == 는 다르다
 
= : 할당 
== : 두 값을 비교 참 또는 거짓 ( a == b ) 
 

산술 연산자

 
이것 또한 간단하게 생각해야지 쉬운데 
 
말 그대로 더하기 빼기 곱하기 나누기다.
 
하나의 코드에 전부 활용해 보자
 

#include <stdio.h>

int main() {
	int a = 20, b = 3;
	printf("%d + %d = %d\n", a, b, a + b);
	printf("%d - %d = %d\n", a, b, a - b);
	printf("%d * %d = %d\n", a, b, a * b);
	printf("%d / %d = %d\n", a, b, a / b);
	printf("%d %% %d = %d\n", a, b, a % b);
	return 0;
}

 
출력결과는 다음과 같을 것이다.
 
20 + 3 = 23 (20 에서 3을 더한 결과)
20 - 3 = 17 (20 에서 3을 뺀 결과)
20 * 3 = 60 (20 에서 3을 곱한 결과)
20 / 3 = 6 (20 에서 3을 나눈 결과)
20 % 3 = 2 (20 에서 3을 나눌 때 나머지 값!)
 
(****추가****)
 

#include <stdio.h>

int main() {
	int a = 2;
	if (a % 2 == 0)  // a % 2 로 나눈 나머지 값이 0 인 경우 
		printf("짝수"); // 짝

	else 
		printf("홀수"); // 그렇지 않다면 홀 수
	
	return 0;
}

 
나머지 연산자(%)를 이용하여 간단한 홀 짝 코드를 만들 수 있다.
 
if는 추후 설명
 

관계 연산자

 
참과 거짓을 판단한다.
쉽게 말해서 이 부분이 논리적으로 진실인지 거짓인지 판단하는 것이다.
저번에도 말했던 것처럼 컴퓨터는 참과 거짓을 숫자 0(False) 1(True)로 표기하니 알아두도록 하자
 

  • >, < : 크다 혹은 작다.
  • == : 같다.
  • != : 다르다.
  • >=, <= : 크거나 같다, 작거나 같다.
#include <stdio.h>

int main() {
	int a = 5, b = 5, c = 3;

	printf("----0은 거짓 1은 참----\n");

	printf("A와 B는 같다 : %d\n", a == b);  // 두 값이 같으므로 참
	printf("A와 C는 다르다 : %d\n", a != c); // 두 값이 다르므로 참
	printf("C는 A보다 크다 : %d\n", a <= c);   // C는 A보다 작은 값이니 거짓
	printf("A는 C보다 크다 : %d\n", a >= c); // A는 C보다 크니 참

}

 

이 때 사용된 관계연산자를 유심히 확인하자

 

논리 연산자

 
이것도 참 혹은 거짓을 판단하는 연산자이다. 집합에 대한 결과를 연산한다.
판단과 출력은 관계연산자를 생각하면 편하다.
 

  • ! : NOT 연산 True를 False로 False를 True로 출력한다.
  • && : AND 연산 두 값이 모두 참일 때 참을 출력한다.
  • || : OR 연산 두 값 중 하나라도 참이라면 참을 출력한다.

 
NOT 연산

INPUTOUTPUT
10
01

 
AND 연산

INPUTINPUTOUTPUT
111
100
010
000

 
OR 연산

INPUTINPUTOUTPUT
111
11
011
000

 
이해가 가는가? 난  처음에 봤을 때 무슨 소린가 했다.
 
쉽게 얘기해서 이러한 조건이 있다.
 
키가 180 이상 이면서 몸무게가 80Kg 이상 인 사람을 뽑으려면 AND를 사용
키가 180 이상 이거나 몸무게가 80Kg 이상 인 사람을 뽑으려면 OR를 사용
 

#include <stdio.h>

int main() {
	int A_height = 180;
	int A_weight = 75;
	int B_height = 184;
	int B_weight = 91;

	printf("키가 180이상 이면서 몸무게가 80Kg 이상 : %d\n", (A_height >= 180) && (A_weight >= 80));
	printf("키가 180이상 이거나 몸무게가 80Kg 이상 : %d\n", (A_height >= 180) || (A_weight >= 80));

	printf("키가 180이상 이면서 몸무게가 80Kg 이상 : %d\n", (B_height >= 180) && (B_weight >= 80));
	printf("키가 180이상 이거나 몸무게가 80Kg 이상 : %d\n", (B_height >= 180) || (B_weight >= 80));

	// 프로그래밍도 수학 처럼 괄호로 묶은 연산이 먼저 연산된다.
}

 

 
만들면 다음과 같이 될 것이다.
 
근데 여기서 NOT연산자를 쓰면?
 

#include <stdio.h>

int main() {
	int A_height = 180;
	int A_weight = 75;
	int B_height = 184;
	int B_weight = 91;

	printf("키가 180이상 이면서 몸무게가 80Kg 미만: %d\n", (A_height >= 180) && !(A_weight >= 80));
	printf("키가 180미만 이거나 몸무게가 80Kg 이상 : %d\n", !(A_height >= 180) || (A_weight >= 80));

	printf("키가 180이상 이면서 몸무게가 80Kg 미만: %d\n", (B_height >= 180) && !(B_weight >= 80));
	printf("키가 180미만 이거나 몸무게가 80Kg 이상 : %d\n", !(B_height >= 180) || (B_weight >= 80));

}

 
이 코드는 기존 코드에 논리 연산자 '!' (부정)을 추가하여 조건을 수정한 것이다.
이제 몸무게가 80kg 미만인 경우와 키가 180cm 미만인 경우를 확인할 수 있다.
 

 
! 는 보는 것처럼 연산을 수행할 곳 앞에 삽입하면 연산결과의 부정으로 리턴된다.
 
근데 사실 이건 괄호만 바꾸면 되는 거라 굳이 이렇게 사용할 필요는 없다.
 

증감 감소

 
값을 증가시키거나 감소시킬 때 사용한다.
 
++ 는 특정한 변수에 1을 더하겠다는 뜻
--는 특정한 변수를 뺀다는 뜻
 

  • ++(변수) : n의 값을 1 증가시킨 후에 증가된 값을 반환
  • (변수)++ : n의 값을 1 증가시킨 후에 증가되기 전의 값을 반환
  • --(변수) : n의 값을 1 감소시킨 후에 감소된 값을 반환
  • (변수)-- : n의 값을 1 감소시킨 후에 감소되기 전의 값을 반환

++(변수) 이런 경우는 값을 바로바로 증가시키는 거라 이해하겠는데.. (변수)++는 뭘까?
 
증감이나 감소가 된 값을 저장만 해두고 다음 출력할 때 영향을 주는 것이다. 그럼 다음 코드를 봐보자
 

#include<stdio.h>

int main() {
	int a = 7;
	printf("%d\n", ++a);
	printf("%d\n", a++);
	printf("%d\n", ++a);
	printf("%d\n", --a);
	printf("%d\n", a--);
	printf("%d\n", --a);
	printf("%d\n", ++a);
	return 0;
}

 
한 줄씩 한 줄씩 생각해 보자
 
이건 따로 출력결과를 첨부하지 않겠다.
생각하고 직접 코드를 쳐서 결과를 보자
 

삼항 연산자

 
한 줄로 조건문을 컷낼 수 있다. 이후 배울 IF문에 축소버전이다.
 

#include <stdio.h>

int main() {
	int a = 1, b = 1; 
	printf("%s\n", (a == b) ? "True" : "Fales"); 
}

 

 
괄호를 이용해서 수식을 넣어주고
물음표를 넣어서 그 값이 참이라면 왼쪽값이 아니라면 오른쪽값이 리턴된다.
 

비트 연산자

 

이 비트 아님

 
오우 쉽게 설명하기 빡센걸~?
 
우선 앞서 말했듯이 컴퓨터는 2진수 0과 1로 이루어져 있다. 이런 걸 비트라고 한다.
 

비트 : 컴퓨터 메모리 중 가장 작은 값 1(bit)

 
이러한 비트를 이용한 진수 연산을 하는 것인데...
 
이참에 2진수 표기를 알아보자 네트워크 할 때도 쓰고 코딩할 때도 쓰고 다양하게 쓰이는 컴공에 기본이라 알아가야 좋다....
 

2^3  2^2  2^1  2^0
 8    4    2    1
-----------------
   0    0    0    0    (0)
   0    0    0    1    (1)
   0    0    1    0    (2)
   0    0    1    1    (3)
   0    1    0    0    (4)
   0    1    0    1    (5)
   0    1    1    0    (6)
   0    1    1    1    (7)
   1    0    0    0    (8)
 ... 이하 생략 ...
 

오른쪽부터 1 2 4 8 16 32 64 128 256.... 이런 식으로 2에 거듭제곱 형태로 나타난다.
 
그렇다는 건 숫자 6을 표현하고 싶다면 00110 이런 식으로 2와 4만 키면 된다..!
 
컴퓨터는 모든 숫자를 위와 같이 표기한다. 
 
예를 들어 네트워크도 이런 식으로 표기한다.
 

2 진수 : 11111111 11111111 11111111 00000000
10 진수 : 255.255.255.0

 
보통 8비트를 4번 사용하는 32bit 시스템이 기본적이다.
 

비트 연산자의 종류

연산자연산자의 기능
&비트단위로 AND 연산을 한다.
|비트단위로 OR 연산을 한다.
^비트단위로 XOR 연산을 한다.
~단항 연산자로서 피연자의 모든 비트를 반전시킨다.
<<피연산자의 비트 열을 왼쪽으로 이동시킨다.
>>피연산자의 비트 열을 오른쪽으로 이동시킨다.

 
& 연산자

INPUTOUTPUT
0 & 00
0 & 10
1 & 00
1 & 11

 
| 연산자

INPUTOUTPUT
0 | 00
0 | 11
1 | 01
1 | 11

 
^ 연산자
두 비트가 서로 다를 때 결과가 1이 되고, 그렇지 않으면 결과가 0이 된다.

INPUTOUTPUT
0 ^ 00
0 ^ 11
1 ^ 01
1 ^ 10

 
~ 연산자
NOT(!) 생각하면 쉬움
부호를 다 바꿔버림 0은 1로 1은 0으로

INPUTOUTPUT
~ 01
~ 10

 
<< 연산자
 
비트를 왼쪽으로 이동시킴
15 :  00000000 00000000 00000000 00001111
30 :  00000000 00000000 00000000 00011110
60 :  00000000 00000000 00000000 00111100
120 : 00000000 00000000 00000000 01111000
 
비트를 한번 왼쪽으로 이동하면 두 배가 된다.
 
>> 연산자
 
비트를 오른쪽으로 이동시킴
15 : 00000000 00000000 00000000 00001111
7 : 00000000 00000000 00000000 00000111 
3 : 00000000 00000000 00000000 00000011 
1 : 00000000 00000000 00000000 00000001 
 
비트를 한번 오른쪽으로 이동하면 값이 절감된다.
 
다음과 같이 비트를 한 번 오른쪽으로 이동하면 값이 절반이 되고, 두 번 이동하면 1/4이 되며, 세 번 이동하면 1/8이 된다.
 
이제 코드를 봐볼까?
 

#include <stdio.h>

int main() {
    int a = 12; // 이진수로 1100
    int b = 25; // 이진수로 11001

    int result_and = a & b; // 1100 & 11001 = 01000 (8)
    int result_or = a | b;  // 1100 | 11001 = 11101 (29)
    int result_xor = a ^ b; // 1100 ^ 11001 = 10101 (21)
    int result_not = ~a;    // ~1100 = 11111111111111111111111111110011 (-13, 32비트 시스템 기준)

    int result_left_shift = a << 2;  // 1100 << 2 = 110000 (48)
    int result_right_shift = b >> 2; // 11001 >> 2 = 00110 (6)

    printf("AND 연산 결과: %d\n", result_and);
    printf("OR 연산 결과: %d\n", result_or);
    printf("XOR 연산 결과: %d\n", result_xor);
    printf("NOT 연산 결과: %d\n", result_not);
    printf("왼쪽 시프트 연산 결과: %d\n", result_left_shift);
    printf("오른쪽 시프트 연산 결과: %d\n", result_right_shift);
    return 0;
}

 

 
비트를 왼쪽 오른쪽 비교 그냥 지지고 돌돌 볶아서 힘들 수 있지만
앞서 설명한 32비트 2진수 체계와 연산자를 이해했다면 이해가 가능할 것이다.
 
(****추가****)
 
이경우 부호가 있는 정수인 산술 시프트인데 부호가 없는 정수인 경우 논리 시프트도 있다.
 

#include <stdio.h>
#include <stdint.h>

int main() {
    int32_t num = -8; // 부호 있는 32비트 정수
    uint32_t unum = -8; // 부호 없는 32비트 정수

    int32_t arithmetic_shift = num >> 1;
    uint32_t logical_shift = unum >> 1;

    printf("산술 오른쪽 시프트: %d\n", arithmetic_shift);
    printf("논리 오른쪽 시프트: %d\n", logical_shift);

    return 0;
}

 

 
논리 시프트 설명
https://ko.wikipedia.org/wiki/%EB%85%BC%EB%A6%AC_%EC%8B%9C%ED%94%84%ED%8A%B8
 
이건 그냥 훑어보기만 하자
 


어쩌다 보니 연산자를 설명하며 이진수 체계까지 설명하게 되었다..
나름대로 쉽게 설명하려고 노력한 건데 잘 전해졌는지 모르겠다.. 나의 마음..
비트 연산자만 조금 신경 써서 공부하면 괜찮을 것이다.
 
다음은 드디어 "입력" 이란 것을 해보자.