C언어 - 연산자와 수식

Jocy·2022년 4월 29일
0
post-thumbnail

기본 연산자

C언어의 기본 연산자는 덧셈(+), 뺄셈(-), 곱셈(*), 나눗셈(/), 나머지(%)가 있습니다.

덧셈, 뺄셈, 곱셈

덧셈, 뺄셈, 곱셈은 출력할 때 결과 값을 변수에저장하지 않고 출력 할 수 있고 변수에 저장도 가능합니다.
정수실수값을 더한 값은 실수형 자료형에 저장해야 합니다.

나눗셈

나눗셈은 / 기호를 이용합니다.
정수끼리 나눗셈을 하면 몫이 결과 값이 되며, 나머지 값은 버려집니다. ex) 10/3 = 3
실수에 나눗셈을 하면 나머지값까지 저장됩니다. ex) 10/3 = 3.333333

나머지

나머지는 나눗셈을 한 뒤에 남는 수이며 % 기호를 사용합니다.
나머지 연산은 정수와 정수끼리만 가능합니다.


증감 연산자

값을 1씩 증가시키거나 감소시키는 역할로 전위 연산자후위 연산자가 있습니다.

전위 연산자

먼저 값을 증가시키거나 감소한 후에 연산자가 사용된 코드를 실행

#include <stdio.h>
int main()
{
  int a = 3
  printf("%d", ++a) // a에서 1이 증가된 4가 출력
  
  return 0
}

후위 연산자

연산자가 사용된 코드를 먼저 실행하고 후에 값을 증가 시킴

#include <stdio.h>
int main()
{
  int a = 3
  printf("%d", a++) // 3이 출력 되고 a가 1 증가
  
  return 0
}

비교 연산자

비교 연산자는 값의 관계를 비교해서 관계 연산자 라고도 합니다.
두 값이 같은지, 다른지, 큰지, 작은지 등등 여러가지를 비교합니다.

== : a==b a와 b는 같다
!= : a!=b a와 b는 다르다
< : a<b a가 b보다 작다
> : a>b a가 b보다 크다
<= : a<=b a가 b보다 작거나 같다
>= : a>=b a가 b보다 크거나 같다

비교 연산자 사용예시

#include <stdio.h>

int main()
{
	int number = 5;

	printf("number 는 5와 같습니까? %d\n", number == 5);  // 같으므로 1(참)
	printf("number 는 4와 같습니까? %d\n", number == 4);  // 같지 않으므로 0(거짓)

	printf("number 는 5와 다릅니까? %d\n", number != 5); // 같으므로 0(거짓)
	printf("number 는 4와 다릅니까? %d\n", number != 4); // 같지 않으므로 1(참)

	printf("number 가 4보다 큽니까? %d\n", 4 < number);    // 크므로 1(참)
	printf("number 가 5보다 큽니까? %d\n", 5 < number);    // 크지 않으므로 0(거짓)
	printf("number 가 10보다 큽니까? %d\n", 10 < number);   // 크지 않으므로 0(거짓)

	printf("number 가 10보다 작습니까? %d\n", number < 10); // 작으므로 1(참)
	printf("number 가 5보다 작습니까? %d\n", number < 5);   // 작지 않으므로 0(거짓)
	printf("number 가 4보다 작습니까? %d\n", number < 4);  // 작지 않으므로 0(거짓)

	printf("number 가 5보다 크거나 같습니까? %d\n", 5 <= number); // 크거나 같으므로 1(참)
	printf("number 가 5보다 작거나 같습니까? %d\n", number <= 5); // 작거나 같으므로 1(참)

	return 0;
}

논리 연산자

참과 거짓 등의 논리 연산할 때 사용되는 논리 연산자 입니다.
논리 연산자에는 논리곱 연산(&&), 논리합 연산(||)이 존재합니다

논리곱 연산(&&)은 양쪽 둘다 참이되면 결과값이 참(1)이 됩니다.
논리합 연산(||)은 둘중에 하나만 참이되면 결과값이 참(1)이 됩니다.

복합 대입 연산자

특정 변수를 선언하고 특정 값을 대입한 연산자
복합 대입 연산자에는 덧셈, 뺄셈, 나눗셈, 나머지 등 심지어 비트 연산자에도 동일하게 적용할 수 있습니다.

복합 연산자 사용예시

#include <stdio.h>

int main()
{
  int a = 3;
  a += 7; // a = a+7 과 같습니다.
  
  printf("%d", a);
  
  return 0;
}

복합 대입 연산자 a += 7 는 a에 7을 더한 값을 다시 a 에 대입하라는 뜻으로
a = a + 7 를 축약시킨 형태입니다.

비트 연산자

비트 연산은 정수나 정수로 변환 가능한 타입만 가능합니다.
실수나 포인터 등은 비트 연산을 할 수 없습니다.

비트는 바이트 단위보다 더 작은 단위이고 컴퓨터에서 사용할 수 있는 최소 단위 0과 1을 사용하는 2진수 입니다.
다른 연산자들처럼 흔하게 사용되는 것은 아니지만 적절한 때에 사용하면 메모리 공간의 효율성을 높이고 연산의 수를 줄일 수 있습니다. 비트 단위로 계산하기 때문에 일반적인 사칙연산 연산자보다 훨씬 속도가 빠릅니다.

비트 연산자 종류

& : AND 연산 : 두개의 비트가 모두 1일때 1을 반환
| : OR 연산 : 두 개의 비트 중 하나라도 1이면 1을 반환
^ : XOR 연산 : 두개의 비트가 다르면 1을, 같으면 0을 반환
~ : NOT연산(모든 비트 반전) : 비트가 1이면 0을, 0이라면 1을 반환
<< : 지정한 수만큼 비트 열을 왼쪽으로 이동
>> : 지정한 수만큼 비트 열을 오른쪽으로 이동

& 연산자(AND)

비트 연산에서 & 연산자는 a & b 와 같이 쓰여집니다.
& 연산은 두개의 비트가 모두 1일때 1을 반환합니다.
A = 1, B = 1 이면 결과값이 1이 나오고 둘중에 하나라도 1이 아니면 0이 나옵니다.

논리 연산자와 비슷하게 생겼지만 논리 연산은 true와 false를 반환하고 비트 연산은 값을 반환 합니다.
&는 주소값을 가리키지만 주소값을 가리키는 연산자는 단항 연산자로써 &b 와 같이 피연산자가 한개만 필요합니다.

& 연산자 사용 예시

#include <stdio.h>

int main()
{
	unsigned char a = 4;  // 0000 0100
	unsigned char b = 8;  // 0000 1000
	unsigned char c = a & b;
	
	printf("4와 8의 AND 연산 값 : %d\n", c); // 4와 8의 AND 연산 값 : 0
	
	a = 6;  // 0000 0110
	b = 13; // 0000 1101
	c = a & b;
	
	printf("6과 13의 AND 연산 값 : %d", c); // 6과 13의 AND 연산 값 : 4
	
	return 0;	
}

예제에서 사용된 자료형들은 int이고 정수인데 굳이 unsigned char를 사용한 것은
char는 자료형에서 설명했듯이 8비트 변수로 signed의 경우 -128 ~ 127까지, unsigned의 경우 0 ~ 255까지 사용이 가능합니다. int의 경우는 32비트 변수로 -2,147,483,648 ~ 2,147,483,647까지 사용이 가능합니다.
비트연산을 쉽게 이해하기 위해 굳이 32비트까지 쓸 필요가 없어서 char를 적용했습니다.

| 연산자(OR)

비트 연산에 | 연산(OR) 두 개의 비트 중 하나라도 1이면 1을 반환 합니다. 논리합 연산과 비슷합니다.

| 연산자 사용 예시

#include <stdio.h>

int main(void)
{
	unsigned char a = 4;  // 0000 0100
	unsigned char b = 8;  // 0000 1000
	unsigned char c = a | b; // 0000 1100
	
	printf("4와 8의 OR 연산 값 : %d\n", c); // 4와 8의 OR 연산 값 : 12
	
	a = 6;  // 0000 0110
	b = 13; // 0000 1101
	c = a | b; // 0000 1111
	
	printf("6과 13의 OR 연산 값 : %d", c); // 6과 13의 OR 연산 값 : 15
		
	return 0;	
}

비교하는 두 개의 비트 중에 하나라도 1이면 1을 반환하는 것을 볼 수 있습니다.

^ 연산자(XOR)

^ 연산은 XOR 연산입니다. 두개의 비트가 다르면 1을, 같으면 0을 반환합니다.

^ 연산자 사용 예시

#include <stdio.h>

int main(void)
{
    unsigned char a = 4;  // 0000 0100
    unsigned char b = 8;  // 0000 1000
    unsigned char c = a ^ b; // 0000 1100
    
    printf("4와 8의 XOR 연산 값 : %d\n", c); // 4와 8의 XOR 연산 값 : 12
    
    a = 6;  // 0000 0110
    b = 13; // 0000 1101
    c = a ^ b; // 0000 1011
    
    printf("6과 13의 XOR 연산 값 : %d", c); // 6과 13의 XOR 연산 값 : 11
    
    return 0;
}

비교하는 두 개의 비트가 다르면 1을 반환하는 것을 볼 수 있습니다.

~ 연산자(NOT)

~ 연산은 NOT 연산입니다. 비트가 1이면 0을, 0이라면 1을 반환합니다.
AND, OR, XOR과는 다르게 피연산자가 하나 입니다.

~ 연산자 사용 예시

#include <stdio.h>

int main(void)
{
	unsigned char a = 4;  // 0000 0100
	unsigned char b = 8;  // 0000 1000
	unsigned char c = ~a; // 1111 1011
	unsigned char d = ~b; // 1111 0111

	printf("4와 8의 NOT 연산 값 : %d, %d\n", c, d); // 4와 8의 NOT 연산 값 : 251, 247

	a = 6;  // 0000 0110
	b = 13; // 0000 1101
	c = ~a; // 1111 1001
	d = ~b; // 1111 0010

	printf("6과 13의 NOT 연산 값 : %d, %d", c, d); // 6과 13의 NOT 연산 값 : 249, 242

	return 0;	
}

~ 연산자에서 unsigned char를 사용하지 않고 부호 있는 정수 signed char 를 사용하게 되면 맨 왼쪽 비트는 부호비트라서 음수<->양수로 변경됩니다. 이 부호비트를 MSB(Most Significant Bit)라 부르며 0이면 양수, 1이면 음수를 나타냅니다. 따라서 MSB가 1이 되면 음수로 계산되어 추가적으로 보수연산을 하게 됩니다. 보수연산으로 인해 부호비트에 영향을 주지 않도록 unsigned를 이용하는 것이 좋습니다.

비트 이동 연산자

왼쪽으로 이동하는 << 연산자와 오른쪽으로 이동하는 >> 연산자가 있습니다.
<< 연산자는 지정한 횟수대로 비트의 자리를 왼쪽으로 이동시키고
>> 연산자는 지정한 횟수대로 비트의 자리를 오른쪽으로 이동시키는 연산자 입니다.

<< 연산자 (left shift) 사용 예시

#include <stdio.h>

int main(void)
{
	char a = 1 << 8;  // 0000 0001
	int b = 1 << 8;   // 0000 0000 0000 0000 0000 0000 0000 0001
    
	printf("(char) 1 << 8 : %d\n", a); // (char) 1 << 8 : 0
	printf("(int) 1 << 8 : %d\n", b); // (int) 1 << 8 : 256
	
	unsigned char c = 1 << 7;
	char d = 1 << 7;
	
	printf("(unsigned) 1 << 7 : %d\n", c); // (unsinged) 1 << 7 : 128
	printf("(signed) 1 << 7 : %d\n", d); // (signed) 1 << 7 : -128

}

char 형은 8비트라서 8자리 이동하면 1이 잘려서 0이 됩니다.
int 형은 32비트라서 8자리 이동하면 1이 잘리지 않고 256이 됩니다.
unsinged char 인 c는 정상적으로 128이 되지만 signed char의 경우 마지막 비트가 MSB이기 때문에 -128이 됩니다.
비트 이동할 때는 자료형을 고려하는 것이 좋습니다.

>> 연산자 (right shift) 사용 예시

#include <stdio.h>

int main(void){
	unsigned char a = 4 >> 1;  // 0000 0100
	unsigned char b = 8 >> 2;  // 0000 1000
    
	printf("4 >> 1 : %d\n", a); // 4 >> 1 : 2
	printf("8 >> 2 : %d\n", b); // 8 >> 2 : 2
	
	a = 14 >> 3;  // 0000 1110
	b = 16 >> 4; // 0001 0000
  
	printf("14 >> 3 : %d\n", a); // 14 >> 3 : 1
	printf("16 >> 4 : %d\n", b); // 16 >> 4 : 1
	
	char c = -16 >> 2;  // 1111 0000
	
	printf("-16 >> 2 : %d\n", c); //  -16 >> 2 : -4
}

오른쪽으로 이동하면서 생기는 빈 비트들을 채워야 하는데, 양수일 때는 unsigned 이든 signed든 0이 채워져도 상관이 없습니다. 그러나 signed 자료형이고 음수일 때에는 맨 왼쪽값이 1이어야 하는데, 이것을 0으로 채워야 할까요, 음수를 유지하기 위해 1로 채워야 할까요?
c의 경우, -16 (1111 0000) 을 2칸 오른쪽으로 이동하면 -4 (1111 1100) 가 됩니다. CPU에서 MSB와 같은 1로 채워줍니다. 하지만 CPU에 따라서 어떤 CPU는 음수에 상관없이 무조건 0을 채우는 CPU가 있습니다. 그래서 right shift 할 경우에 이러한 부분을 잘 고려해야 합니다.

profile
Software Engineer

0개의 댓글