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을 반환
<<
: 지정한 수만큼 비트 열을 왼쪽으로 이동
>>
: 지정한 수만큼 비트 열을 오른쪽으로 이동
비트 연산에서 &
연산자는 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) 두 개의 비트 중 하나라도 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 연산입니다. 두개의 비트가 다르면 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 연산입니다. 비트가 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를 이용하는 것이 좋습니다.
왼쪽으로 이동하는
<<
연산자와 오른쪽으로 이동하는>>
연산자가 있습니다.
<<
연산자는 지정한 횟수대로 비트의 자리를 왼쪽으로 이동시키고
>>
연산자는 지정한 횟수대로 비트의 자리를 오른쪽으로 이동시키는 연산자 입니다.
#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이 됩니다.
비트 이동할 때는 자료형을 고려하는 것이 좋습니다.
#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 할 경우에 이러한 부분을 잘 고려해야 합니다.