비트 연산자와 그 외 여러 연산자들에 대해 알아보자

2024. 12. 31. 13:00JAVA

728x90
반응형

 

 안녕하세요. 진득 코딩입니다.

 

 이번 시간에는 비트 연산자와 그 외에 여러 연산자들에 대해서 알아보도록 하겠습니다.

비트 연산자

 

  • 비트 연산자는 피연산자를 비트 단위로 논리 연산하는 연산자입니다.

  • 피연산자를 이진수로 표현했을 때의 각 자리를 규칙에 다라 연산을 수행합니다.

  • 피연산자로 실수는 허용하지 않고 정수(문자 포함)만 허용합니다.


 | (OR연산자) 피연산자 중 한쪽의 값이 1이면, 1을 결과로 얻습니다. 그 외에는 0을 얻습니다.

 & (AND연산자) 피연산자 양 쪽이 모두 1이어야만 1을 결과로 얻습니다. 그 외에는 0을 얻습니다.

 ^ (XOR연산자) 피연산자의 값이 서로 다를 때만 1을 결과로 얻습니다. 같을 때는 0을 얻습니다.

 

x y x | y x & y x ^ y
1 1 1 1 0
1 0 1 0 1
0 1 1 0 1
0 0 0 0 0

 

  • 연산자 '^'는 배타적 XOR(eXclusive OR)라고 하며, 피연산자의 값이 서로 다른 경우, 즉 배타적인 경우에만 참(1)을 결과로 얻습니다.

  • 비트 OR연산자 '|'는 주로 특정 비트의 값을 변경할 때 사용합니다.

  • 비트 AND연산자 '&'는 주로 특정 비트의 값을 뽑아낼 때 사용합니다.

  • 비트 XOR연산자 '^'는 두 피연산자의 비트가 다를 때만 1이 됩니다.

  • 같은 값으로 두고 XOR연산을 수행하면 원래의 값으로 돌아오는 특징이 있어 간단한 암호화에 사용합니다.

  • 또한 비트연산에서도 피연산자의 타입을 일치시키는 '산술 변환'이 일어날 수 있습니다.

예제 3-28

 

  • 비트연산의 결과를 2진수로 출력하기 위해 toBinaryString()이라는 메서드를 작성해서 사용하였습니다.

  • 해당 메서드는 4byte의 정수를 32자리의 2진수로 변환합니다.

비트 전환 연산자 ~

 

  • 비트 전환 연산자는 피연산자를 2진수로 표현했을 때, 0은 1로, 1은 0으로 바꿉니다.

  • 논리부정 연산자 '!'과 유사합니다.

x ~x
1 0
0 1

 

  • 비트 전환 연산자 '~'에 의해 비트 전환되고 나면, 부호 있는 타입의 피연산자는 부호가 반대로 변경됩니다.

  • 즉, 피연산자의 '1의 보수'를 얻을 수 있습니다.

  • 그래서 비트전환연산자를 '1의 보수' 연산자라고도 합니다.

예제 3-29

 

  • 위 예제를 보면 양의 정수 p가 있을 때, p에 대한 음의 정수를 얻으려면 '~p + 1'을 계산하면 됩니다.

  • 반대로 음의 정수 n이 있을 때, n에 대한 양의 정수를 얻으려면 '~(n-1)'을 계산하면 됩니다.

  • '~~p'를 계산하면 1을 0으로 바꿨다가 다시 0을 1로 바꾸므로 원래의 값이 됩니다.

  • 연산결과의 타입은 byte가 아니라 int입니다.

쉬프트 연산자 << >>

 

  • 쉬프트 연산자는 피연산자의 각 자리(2진수로 표현했을 때)를 '오른쪽(>>)' 또는 '왼쪽(<<)'으로 이동(shift)한다는 의미를 가지고 있습니다.

  • 자리이동으로 저장범위를 벗어난 값들은 버려진고 빈자리는 0으로 채워지게 됩니다.

  • '<<'연산자의 경우, 피연산자의 부호에 상관없이 각 자리를 왼쪽으로 이동시키며 빈칸을 0으로만 채우게 됩니다.

  • 반면 '>>'연산자는 부호 있는 정수는 부호를 유지하기 위해 왼쪽 피연산자가 음수인 경우 빈자리를 1로 채우게 됩니다.

  • 쉬프트 연산자의 좌측 피연산자는 산술변환이 적용되어 int보다 작은 타입은 int타입으로 자동 변환되고 연산결과 역시 int 타입이 됩니다.

  • 그러나 쉬프트 연산자는 피연산자의 타입을 일치시킬 필요가 없기 때문에 우측 피연산자에는 산술변환이 적용되지 않습니다.

수식 자리이동 연산 결과
2진수 10진수
8 >> 0 없음 00000000 00000000 00000000 00001000 8
8 >> 1 오른쪽으로 한번 00000000 00000000 00000000 00000100 4
8 >> 2 오른쪽으로 두번 00000000 00000000 00000000 00000010 2
-8 >> 0 없음 11111111 11111111 11111111 11111000 -8
-8 >> 1 오른쪽으로 한번 11111111 11111111 11111111 11111100 -4
-8 >> 2 오른쪽으로 두번 11111111 11111111 11111111 11111110 -2
8 << 0 없음 00000000 00000000 00000000 00001000 8
8 << 1 왼쪽으로 한번 00000000 00000000 00000000 00010000 16
8 << 2 왼쪽으로 두번 00000000 00000000 00000000 00100000 32
-8 << 0 없음 11111111 11111111 11111111 11111000 -8
-8 << 1 왼쪽으로 한번 11111111 11111111 11111111 11110000 -16
-8 << 2 왼쪽으로 두번 11111111 11111111 11111111 11100000 -32

 

  • 위 표는 쉬프트 연산의 결과를 2진수와 10진수로 나타낸 것입니다.

  • 위 표를 보면, 2진수 n자리를 왼쪽으로 이동하면 피연산자를 2^n으로 곱한 결과를, 오른쪽으로 이동하면 피연산자를 2^n으로 나눈 결과를 얻는다는 것을 알 수 있습니다.


 x << n은 x*2^n의 결과와 같습니다.

 x >> n은 x/2^n의 결과와 같습니다.

 

  • 곱셈이나 나눗셈 연산자를 사용하지 않고 쉬프트 연산자를 사용하는 이유는 속도 때문입니다.

  • 쉬프트 연산자는 속도가 곱셈이나 나눗셈 연산자보다 빠르지만 가독성은 떨어집니다.

  • 따라서 쉬프트 연산자보다 곱셈 또는 나눗셈 연산자를 주로 사용하고, 보다 빠른 실행속도가 요구되는 곳만 쉬프트 연산자를 사용하는 것이 좋습니다.

예제 3-30
예제 3-30 console 출력

  • 쉬프트 연산자를 사용하여 값이 바뀌는 과정들을 살펴볼 수 있습니다.

  • 쉬프트 연산자를 32번 사용하면 원래의 값으로 돌아오게 됩니다.

예제 3-31

 

  • int mask로 0xF를 사용하는 이유는 0xF가 1111로 끝나기 때문에 & 연산자로 특정 값을 뽑아낼 수 있기 때문입니다.

  • 쉬프트 연산자로 4자리를 옮길때마다 다음 숫자가 출력되는 것을 확인할 수 있습니다.

  • 위 과정으로 쉬프트 연산자와 비트AND연산을 이용하여 16진수의 각 자리를 하나씩 얻어올 수 있음을 알게 되었습니다.

조건 연산자 ?:

 

  • 조건 연산자는 조건식, 식1, 식2 모두 세 개의 피연산자를 필요로 하는 삼항 연산자이며, 삼항 연산자는 조건 연산자 하나뿐입니다.

  • 조건 연산자는 첫 번째 피연산자의 조건식의 평가결과에 따라 다른 결과를 반환합니다.

  • 조건식의 평가결과가 true이면 식1이, false이면 식2가 연산결과가 됩니다.

  • 조건 연산자는 조건문인 if문으로 바꿔 쓸 수 있으며, if문 대신 조건 연산자를 사용하면 코드를 보다 간단히 할 수 있습니다.

조건 연산자 식 조건문 if문
result = ( x > y ) ? x : y;
if( x > y)
result = x; // x > y가 true일 때
else
result = y; // x > y가 false일 때

 

  • 한눈에 봤을 때도 조건 연산자를 사용한 식이 더 간단합니다.

  • 조건 연산자를 중첩해서 사용하면 셋 이상 중의 하나를 결과로 얻을 수 있습니다.


result = x >  0 ? 1 : ( x == 0 ? 0 : -1);

 

  • 조건 연산자를 여러 번 중첩하면 코드가 간략해지긴 하지만, 가독성이 떨어지므로 꼭 필요한 경우에 한번 정도만 중첩하는 것이 좋습니다.

  • 또한 조건 연산자의 식1과 식2의 피연산자의 타입이 다른 경우, 이항 연산자처럼 산술 변환이 발생합니다.

예제 3-32

 

  • 조건 연산자를 이용해서 변수의 절대값을 구한 후, 부호를 붙여 출력하는 예제입니다.

대입 연산자 =

 

  • 대입 연산자는 변수와 같은 저장공간에 값 또는 수식의 연산결과를 저장하는 데 사용합니다.

  • 대입 연산자는 오른쪽 피연산자의 값 (식이라면 평가값)을 왼쪽 피연산자에 저장합니다.

  • 그 후 저장된 값을 연산결과로 반환합니다.

  • 대입 연산자는 연산자들 중에서 가장 낮은 우선순위를 가지고 있기 때문에 식에서 제일 나중에 수행됩니다.

  • 또한 연산 진행 방향이 오른쪽에서 왼쪽이기 때문에 뒤에서부터 앞으로 대입되는 꼴입니다.

lvalue와 rvalue

 

  • 대입 연산자의 왼쪽 피연산자를 'lvalue(left value)'이라 하고, 오른쪽 피연산자를 'rvalue(right value)'라고 합니다.

  • rvalue는 변수뿐만 아니라 식이나 상수 등이 모두 가능하지만 lvalue는 반드시 변수처럼 값을 변경할 수 있는 것이어야 합니다.

  • 리터럴이나 상수같이 값을 저장할 수 없는 것들은 lvalue가 될 수 없습니다.

복합 대입 연산자

 

  • 대입 연산자는 다른 연산자(op)와 결합하여 'op='와 같은 방식으로 사용할 수 있습니다.

  • 결합된 두 연산자는 반드시 공백 없이 붙여 써야 합니다.

op= =
i += 3; i = i + 3;
i -= 3; i = i - 3;
i *= 3; i = i * 3;
i /= 3; i = i / 3;
i %= 3; i = i % 3;
i <<= 3; i = i << 3;
i >>= 3; i = i >> 3;
i &= 3; i = i & 3;
i ^= 3; i = i ^ 3;
i |= 3; i = i | 3;
i *= 10 + j; i = i * (10 + j);

 

  • 왼쪽은 복합 연산자의 사용 예이고, 오른쪽은 대입 연산자를 이용한 왼쪽과 동일한 의미의 식입니다.

  • 마지막 줄에서 ' i *= 10 + j;'를 ' i = i * 10 + j;'와 같은 것으로 오해하지 않도록 주의해야 합니다.


 

이번 포스팅을 마지막으로 연산자에 대해 모두 알아보았습니다.

 프로그래밍에서 연산이라는 것은 기본이며 이러한 연산을 도와주는 연산자들을 숙지하여 적절하게 잘 사용하여 좋은 프로그램을 만들길 바랍니다.

 

 이번 포스팅은 여기까지입니다.

 

 이해가 안 되시는 부분이 있거나 잘못된 내용에 대해 지적해 주실 분들은 댓글로 남겨주시면 답글 달아드리겠습니다.

 끝까지 봐주셔서 감사합니다.😊

728x90
반응형
LIST