SturdyCobble's Study Note

[프로그래밍] 4.2 연산자(2) - 관계, 논리, 비트 연산자 본문

휴지통/['19.06-'20.07]프로그래밍101

[프로그래밍] 4.2 연산자(2) - 관계, 논리, 비트 연산자

StudyingCobble 2019. 8. 13. 16:49

NOTICE : 독학인 만큼, 잘못된 내용이 있을 수 있습니다. 내용 지적은 언제나 환영합니다.

더 이상 이 블로그에는 글을 올리지는 않을 예정입니다. 그렇지만 댓글을 달아주시면 최대한 답변드리고자 노력하겠습니다.


※이 글은 프로그래밍 언어에 대한 기초적인 이해를 가정하고 있습니다. 최소 프로그래밍 언어 하나 정도를 약간이라도 접해보시는 것을 추천합니다. 또한, 이 글은 심화 내용은 되도록 피하여 서술했습니다.

 

이번 글에서는 관계, 논리, 비트 연산자와 관련된 내용에 대해 소개하려고 합니다.

 

 먼저 관계 연산자와 논리 연산자는 if문 글에서 이미 자세히 설명한 바 있습니다. 그렇기 때문에 각각에 대한 설명은 생략하겠습니다. 다만 여러 연산자를 혼합해서 설명했기 때문에, 표로서 다음과 같이 정리해 보여드리겠습니다.

 

예전 글 링크:

2019/08/03 - [프로그래밍/프로그래밍101] - [프로그래밍] 3.1 제어문(1) - if문

 

분류 연산자 설명 C/C++, Java 연산자 Python 연산자
관계

좌변 > 우변인지

> >
좌변 < 우변인지
< <
좌변 ≥ 우변인지 >= >=
좌변 ≤ 우변인지 <= <=
좌변 = 우변인지 == ==
좌변 ≠ 우변인지 != !=
논리 (조건)의 부정 ! not
(조건) 이 모두 참인지 && and
 (조건)이 적어도
하나가 참인지
|| or

(연산자 형태인 것만 가지고 왔습니다.)

 

Python에는 다음과 같은 메소드들도 존재합니다.

 

분류 연산자 설명 대응되는 Java 메소드 Python 연산자
멤버 포함여부  .contain 메소드  in
식별 메모리 위치 비교  .equals 메소드
(String 자료형의 경우)
is

 

이 또한, 연산자이므로, 우리가 덧셈 뺄셈을 진행하면 그 값이 반환되듯이 연산 결과 참과 거짓이라는 값이 반환된다고 생각할 수 있습니다. 

 

 식별 연산자에 대해서 좀 더 알아보겠습니다. 식별 연산자는 메모리 위치를 가지고 비교를 진행합니다. 이때 문자열은 메모리의 특정 위치에 저장되어 계속 참조되므로 아래 코드에서 True가 출력됩니다. id함수는 저장된 주소를 가져오는 함수입니다.

 

a = "Plastic"
b = "Plastic"
print(a is b)
print(id(a))
print(id(b))
print(id("Plastic"))
-------------출력 결과------------
True
57545984
57545984
57545984

 

다만 이와 비슷하게 C++로 진행하면, 다른 결과가 나옵니다. 다만, 문자열이 메모리에 저장되었다는 사실을 &연산를 통해 알 수 있습니다.

 

string str1 = "He";
string str2 = "He";
cout<<&str1<<" "<<&str2<<" "<<(str1==str2)<<" "<<&"He"<<endl;
---------------출력 결과-------------
0x61fef4 0x61fedc 1 0x405045

 

반대로 정수는 따로 메모리에 저장되지 않습니다. 사실 Python의 경우 -5~256의 정수만 메모리에 저장됩니다. 그래서 만약 파일을 생성하지 않고 대화형으로 프로그래밍을 한다면 다음과 같이 1000에 대한 주소가 계속 변하는 것을 보실 수 있습니다. (파일을 생성한다면 문제가 되지 않습니다.)

 

>>> id(1000)
26664160
>>> id(1000)
26664176
>>> id(1000)
26661248

 

C언어에선 아예 다음과 같은 코드는 불가능합니다. 정수 자체가 상수로서 어딘가 저장된 것이 아니기 때문입니다.

 

cout<< &(1) << endl;  //에러 발생!

 

식별 연산자에 관한 자세한 내용과 메모리 저장 방식에 관한 자세한 설명은 이후 문자열 파트나 포인터 파트에서 다시 진행하겠습니다.

 

 그 다음은 비트 연산자입니다. 비트 연산자는 정수를 가지고 비트 단위로 연산을 하며, 아무래도 컴퓨터의 기본 언어를 가지고 연산하기에 연산이 더 빠릅니다. 다음과 같은 연산자들이 있습니다.

 

연산자 C/C++/Python 설명 Java 설명 예시
& AND(둘 다 1이여야 1) a&b
| OR(둘 다 0이여야 0) a|b
^ XOR(두 비트가 달라야 1) a^b
~ NOT(비트 반전) ~a
>> 오른쪽으로 주어진 칸만큼 비트 이동(오른쪽에 주어진 칸을 쓰며 10진수로 표기)
(넘어가는 부분은 삭제, 부호 유지(≥0인 수와 <0인 수로 나뉨)
a>>2
<< 왼쪽으로 주어진 칸만큼 비트 이동
(넘어가는 부분은 삭제, 부호 유지)
a<<2
>>>   부호 없는 오른쪽 비트 이동
(부호와 상관없이 무조건 남는 공간을 0으로 채움.
=> 무조건 양수)
a>>>2

 

아래 예시를 참고해봅시다. (bin 함수는 정수를 2진수 문자열로 표현합니다.)

print(bin(34))
print(bin(41))
print(34&41)
print(34|41)
print(34^41)
print(~34)
print(34>>2)
print(34<<2)
print(-34>>2)
------------출력 결과-------------
0b100010
0b101001
32
43
11
-35
8
136
-9

위 코드의 결과를 이해하기 쉽게 그림으로 나타내봤습니다.

 

비트 연산자가 나온 김에 컴퓨터의 음수 저장 시스템을 잠깐 살펴보겠습니다. 컴퓨터가 음수를 표현하는 방법은 부호 비트를 할당하는 법, 1의 보수(그냥 보수), 2의 보수(1의 보수를 구하고 1을 더함)을 이용하는 방법 등이 있으며, 2의 보수가 가장 많이 사용됩니다.

 

 그런데, 이때 부호 비트와 1의 보수 방법은 다음과 같이 0을 표현하는 방법이 두가지가 된다는 문제점이 생깁니다.

따라서 정수의 경우 2의 보수 방법으로 음수를 표현합니다. 그러나 실수는 부호 비트를 가지고 표현하기 때문에 +0과 -0이 둘 다 존재합니다.

 

 

 

 이러한 비트 연산은 가장 기본적인 연산일 뿐만 아니라 속도가 빠르기 때문에 고정된 수에 대한 곱셈 등을 행할 때 속도를 늘리는 효과를 나타낼 수 있습니다. 특히 SHIFT연산을 유심히 보셨다면 알겠지만, 왼쪽 Bitwise shift는 2의 거듭제곱을 곱하는 효과를 나타냅니다.

 

추가적으로 다른 정수에 대한 곱셈을 한다면 다음과 같은 예시들이 가능합니다.

 

AND와 OR, XOR연산은 기본적으로 특정 비트를 1로 만들거나, 0으로 만들거나 반전시키는 역할을 할 수 있습니다. 비트 마스킹이라고 불리는 이 기법을 통해 다양한 알고리즘의 성능을 향상시킬 수 있습니다. 아래 예시는 대소문자 반전을 시킵니다.

 

char CH101 = 'a';
char CH103 = 'A';
//MASK OFF
cout << (CH101) << " can be transformed into " << (char)(CH101 & 223) << endl; //223 = 1101 1111(2) = 0xDF
//MASK ON
cout << (CH103) << " can be transformed into " << (char)(CH103 | 32) << endl; //32 = 0010 0000(2) = 0x20

(비트 연산을 통해 char형이 int형으로 변환되므로, 이를 char형으로 되돌리기 위해 (char)이라는 연산자로 char형으로 변환시켰습니다.)

 

 

 이는 아스키 코드 상 대문자와 소문자가 다음과 같은 관계를 가짐을 이용합니다.

이진수를 십진수로 표현하는게 어렵다면, 16진수로 4자리수로 끊어서 표현하는 것도 좋은 방법입니다.(1111 1111(2)는 1111이 F에 해당하므로 0xFF와 같습니다.)

 

또한, 특정 비트를 조사하고 반전시키기 위해서도 비트 연산을 이용할 수 있습니다.

a = 0b11011101
#오른쪽에서 세번째 비트를 조사
if ( a & 0b00000100 ):
    print("3rd bit is ON")
# 세번째 비트를 반전
a ^= 0b00000100
# 세번째와 네번째 비트를 켜기(ON)
a |= 0b00000100 | 0b00001000
# 다섯번째 비트를 끄기(OFF)
a &= ~0b00010000

이러한 방식으로 여러개의 Boolean 변수를 대신할 수 있습니다. 이와 같이 각각의 Bool값을 표현하는 것을 비트 플래그(Bit Flag)라고 부른다고 합니다.

 

 

Comments