비트 연산자와 그 외 여러 연산자들에 대해 알아보자
2024. 12. 31. 13:00ㆍJAVA
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연산을 수행하면 원래의 값으로 돌아오는 특징이 있어 간단한 암호화에 사용합니다.
- 또한 비트연산에서도 피연산자의 타입을 일치시키는 '산술 변환'이 일어날 수 있습니다.
- 비트연산의 결과를 2진수로 출력하기 위해 toBinaryString()이라는 메서드를 작성해서 사용하였습니다.
- 해당 메서드는 4byte의 정수를 32자리의 2진수로 변환합니다.
비트 전환 연산자 ~
- 비트 전환 연산자는 피연산자를 2진수로 표현했을 때, 0은 1로, 1은 0으로 바꿉니다.
- 논리부정 연산자 '!'과 유사합니다.
x | ~x |
1 | 0 |
0 | 1 |
- 비트 전환 연산자 '~'에 의해 비트 전환되고 나면, 부호 있는 타입의 피연산자는 부호가 반대로 변경됩니다.
- 즉, 피연산자의 '1의 보수'를 얻을 수 있습니다.
- 그래서 비트전환연산자를 '1의 보수' 연산자라고도 합니다.
- 위 예제를 보면 양의 정수 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의 결과와 같습니다.
- 곱셈이나 나눗셈 연산자를 사용하지 않고 쉬프트 연산자를 사용하는 이유는 속도 때문입니다.
- 쉬프트 연산자는 속도가 곱셈이나 나눗셈 연산자보다 빠르지만 가독성은 떨어집니다.
- 따라서 쉬프트 연산자보다 곱셈 또는 나눗셈 연산자를 주로 사용하고, 보다 빠른 실행속도가 요구되는 곳만 쉬프트 연산자를 사용하는 것이 좋습니다.
- 쉬프트 연산자를 사용하여 값이 바뀌는 과정들을 살펴볼 수 있습니다.
- 쉬프트 연산자를 32번 사용하면 원래의 값으로 돌아오게 됩니다.
- 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의 피연산자의 타입이 다른 경우, 이항 연산자처럼 산술 변환이 발생합니다.
- 조건 연산자를 이용해서 변수의 절대값을 구한 후, 부호를 붙여 출력하는 예제입니다.
대입 연산자 =
- 대입 연산자는 변수와 같은 저장공간에 값 또는 수식의 연산결과를 저장하는 데 사용합니다.
- 대입 연산자는 오른쪽 피연산자의 값 (식이라면 평가값)을 왼쪽 피연산자에 저장합니다.
- 그 후 저장된 값을 연산결과로 반환합니다.
- 대입 연산자는 연산자들 중에서 가장 낮은 우선순위를 가지고 있기 때문에 식에서 제일 나중에 수행됩니다.
- 또한 연산 진행 방향이 오른쪽에서 왼쪽이기 때문에 뒤에서부터 앞으로 대입되는 꼴입니다.
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
'JAVA' 카테고리의 다른 글
자바 조건문 switch문에 대해 알아보자 (0) | 2025.01.02 |
---|---|
자바 제어문인 조건문 if문에 대해 알아보자 (0) | 2025.01.01 |
자바 비교 연산자와 논리 연산자에 대해 알아보자 (0) | 2024.12.30 |
자바 산술 연산자에 대해 알아보자 (2) | 2024.12.27 |
자바 연산자(operator) 개념과 종류에 대해 알아보자 (0) | 2024.12.26 |