묵시적 변환
- 기본 자료형 간의 변환을 컴파일러가 알아서 해준다.(모든 기본 자료형 간의 변환이 가능한 것은 아니다.)
- int/long -> float 또는 long -> double로 변환할 때는 손실이 있을 수 있다. (정밀도의 문제가 있다.)
승격(promotion)
변환과 승격은 엄밀히 따지면 다른 것이지만 프로그래머가 프로그램을 짤 때 이 둘을 구분하는 것은 그렇게 중요하지 않다. 중요한 것은 어떤 것에서 어떤 것으로 변환이 되는지, 변환 시 문제가 있는지, 변환 시 2진수 표현이 바뀌는지 아닌지 등의 여부를 아는 것이 중요하다.
- 컴파일러가 자동으로 실수형이나 부동소수형 자료의 이진 표현을 확장한다.
- 산술 연산자 또는 논리 연산자가 제대로 동작하게 하거나 ALU(산술논리장치)가 더 효율적으로 돌 수 있게 하려고 사용한다.
- 예를 들어 int와 double을 더했을 때 정수+실수의 조건인데, double로 해야 소수점 표현이 가능하니까 두 가지의 합은 double로 출력된다.
- 작은 형에서 큰 형으로 정수를 변환할 때 문제가 있는가? 없다.
- 큰 형에서 작은 형으로 변환할 때 문제가 있는가?
- 값이 충분히 작으면 문제가 없지만 실행 중에 예상했던 값이 아닌 다른 값이 들어갈 수 있고, 만약 실행 중에 값이 바껴서 값이 충분히 작지 않다면 컴파일 에러가 날 수 있다.
- 위에서 말한 것처럼 런타임 중에 값이 어떨게 될지 모른다는 상황을
정보의 손실
이라 한다.
명시적 변환
승격이나 묵시적 변환과 같은 변환 시 발생하는 컴파일 에러같은 문제를 해결하려면 명시적 형변환
을 사용하면 된다.
- 명시적 형변환은 소괄호
()
를 사용하여 변환하고자 하는 자료형을 보여준다. - 명시적 형변환은 프로그래머의 의도를 보여준다는 점에서 중요하다.
- e.g. num1, num2는 double로 정의되어 있는데 정수형으로 명시적 형변환을 한다. -> int result = (int)num1 + (int)num2;
- 위의 예시와 같은 형변환시 소수점은 무조건 내림이다.
- 모든 자료형이 변환되는 것은 아니다.
연산자
- + 연산자: 두 피연산자를 더하기
- - 연산자: 두 피연산자를 빼기
- 연산의 결과가 음수일 경우 조심해야 한다.
- 부호 없는 피연산자끼리 빼서 음수가 나오면 예상치 못한 값이 나올 수 있으니 조심해야 한다. (e.g. 언더플로우)
- * 연산자: 두 피연산자를 곱하기,
*
사용. - \/ 연산자: 두 피연산자를 나누기,
/
사용- 정수형의 피연산자는 결과가 제대로 나오지 않을 수 있다.
-1
2
3
4int number1 = 10;
int number2 = 30;
int result1 = number1 / number2; //0
double result2 = number1 / number2; //0 - 위의 예시에서
number1 / number2
는 0.3333…의 결과가 나오는데, int형으로 나타내면 소수점 아래는 모두 버리기 때문에 결과가 0이 된다. 그런데 이 결과는double
에 담으면 0.333…이 결과가 될 것 이라고 생각하지만 이 역시도 결과가 0이다. 그 이유는number1 / number2
를 모두 int라고 생각해서 이 연산 결과 자체를 int로 처리한다. 그래서 int 0을 double에 대입한 셈이 되기 때문에 결과는 0이 된다. - 만약 result2처럼 원치 않는 결과를 얻기 싫으면
명시적 형변환
이 필요하다. e.g. double result3 = (double)number1 / (double)number2; 와 같이 명시적 형변환을 해주면 원래 예상했던 값인 0.333…을 얻을 수 있다.
- 정수형의 피연산자는 결과가 제대로 나오지 않을 수 있다.
- \% 연산자: 나눗셈의 나머지를 구한다. (/연산자는 나눗셈의 몫을 구한다.)
증가/감소 연산자
++ 연산자
- 피연산자 하나의 값을 1증가
- 연산기호를 변수의 앞이나 뒤에 붙일 수 있다.
- num++ 또는 ++num으로 사용.
++
이 어디 위치하느냐에 따라 값이 달라진다. 대입과 증가하는 순서가 다르다.전위 증가 연산자는 증가 후 대입이 이루어지고, 후위 증가 연산자는 대입 후 증가가 이루어진다.
| 전위 증가 연산자 | 후위 증가 연산자 |
| ———————— | ———————— |
| int num = 10; | int num = 10; |
| ++num;//11
| num++;//10
|
| num;//11
| num;//11
|
—연산자
- 피연산자의 값을 1감소
++ 또는 — 연산자를 부동소수점형에 쓴다면 어떻게 될까?
부동소수점형도 증감연산자를 사용할 수 있는데, 예상한 결과를 얻지 못할 수 있으므로 추천되지 않는다. 예를 들어 3.14라는 값을 1증가시켜 4.13로 만들기 위해서 ++연산자를 사용했다고 하면, 기대하던 값과 달리 3.24 또는 3.15와 같은 값이 나올 수 있다. 따라서 웬만하면 증감연산자는 정수형(char, int, long)에 사용하는 것이 좋다.
진법
- 10진법: 0~9
e.g. 1024 = 1 * 103 + 0 * 102 + 2 * 101 + 4 *100 - 2진법: 0,1
e.g. 1101 = 1 * 23 + 1 * 22 + 0 * 21 + 1 * 20 - 8진법: 0~7
e.g. 1703 = 1 * 83 + 7 * 82 + 0 * 81 + 3 * 80 - 16진법: 0~9, a~f
- 0~9 다음 a~f는 10~15이다.
e.g. 1a2 = 1 * 162 + 10 * 161 + 4
- 0~9 다음 a~f는 10~15이다.
진법 변환
바꾸고자 하는 진법의 수를 몫이 0이 될때까지 나눈다. 몫이 0이 된 수의 나머지부터 차례대로 숫자로 표기한다.
e.g. 십진수 3을 2진수로 바꾸고 싶으면, 3을 바꾸고자 2로 나눈다. 10을 2진수로 표기하면 11이다.
어떤 숫자를 각 진법별로 나타내고 싶을 때는 8진수로 먼저 변환한 후 2진수나 16진수로 바꾸는 것이 가장 빠르게 변환하는 방법이다.
비트가 상태를 나타내는데 비트가 1개만 있을 때는 21이고, 2가지 상태를 나타낼 수 있다.
8진수 단위 숫자 하나로 0~7까지 8가지를 표현할 수 있고, 2진수 단위 숫자가 3개가 필요하다.(23 = 8)
2진수에서 4개의 단위 숫자는 16진수 한 개의 단위 숫자로 표현할 수 있다.
- 21 = 2
- 22 = 4
- 23 = 8
- 24 = 16
e.g. 2778 -> 0101111112 -> fb16
비트 연산자
이진수 피연산자를 비트 단위로 연산할 때 사용
&(and), |(or), ^(xor), ~(not)
1 | int op1 = 12; // 0000 0000 0000 0000 0000 0000 0000 1100 |
&(and)
숫자끼리 비교할 때 둘 중 하나라도 0이 있으면 최종 결과는 0이 된다. 둘 다 1일 경우는 1이 된다.
op1 | 1 | 1 | 0 | 0 |
---|---|---|---|---|
op2 | 1 | 0 | 0 | 1 |
AND |
1 | 0 | 0 | 0 |
|(or)
숫자끼리 비교할 때 둘 중 하나라도 1이 있으면 최종 결과는 1이 된다. 둘 다 0일 경우는 0이 된다.
op1 | 1 | 1 | 0 | 0 |
---|---|---|---|---|
op2 | 1 | 0 | 0 | 1 |
OR |
1 | 1 | 0 | 1 |
^(xor)
숫자끼리 비교할 때 두 숫자가 같을 경우는 0이 되고, 다를 경우 1이 된다.
op1 | 1 | 1 | 0 | 0 |
---|---|---|---|---|
op2 | 1 | 0 | 0 | 1 |
XOR |
0 | 1 | 0 | 1 |
~(not)
해당하는 숫자를 뒤집는다. 1이면 0, 0이면 1이 된다.
op1 | 0 | 1 | 1 | 0 | 0 |
---|---|---|---|---|---|
NOT |
1 | 0 | 0 | 1 | 1 |
비트 이동 연산자
비트를 왼쪽 혹은 오른쪽으로 움직이는 연산자들
우항으로 부동소수점 수는 사용 불가 (e.g. int result = result1 << 2.5f;)
<<(left-shift)
: 왼쪽으로 n칸씩 이동한다. 이동하고 나서 남는 숫자는 버리고, 빈 칸은 0으로 채운다. (자릿수를 이동할 때마다 2씩 곱해짐 e.g.int op1 = 12; -> op1 << 1;(24) -> op1 << 2;(48))>>(right-shift)
: 오른쪽으로 n칸씩 이동한다. 이동하고 나서 남는 숫자는 버리고, 앞 비트는 0으로 채운다. (자릿수를 이동할 때마다 2씩 나눠짐)
op1 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 |
---|---|---|---|---|---|---|---|---|
op1 << 1 |
0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 |
op1 << 2 |
0 | 0 | 1 | 1 | 0 | 0 | 0 |
0 |
op1 >> 3 |
0 |
0 |
0 |
0 | 0 | 0 | 0 | 1 |
op1 >> 4 |
0 |
0 |
0 |
0 |
0 | 0 | 0 | 0 |
1 | int op1 = 12; //0000 ... 0000 1100 |
대입 연산자
좌항에 우항의 값을 저장한다. 기호는 =
. 수학의 대입처럼 같다는 의미로 사용되는 것이 아니다.
산술 연산자/비트 연산자와 조합
1 | int num1 = 10; |
References
실무 프로그래밍 입문(C#)