Front-end Developer

0%

자료형 변환, 연산자

묵시적 변환

  • 기본 자료형 간의 변환을 컴파일러가 알아서 해준다.(모든 기본 자료형 간의 변환이 가능한 것은 아니다.)
  • 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
      4
      int 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이 될때까지 나눈다. 몫이 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
2
3
4
5
6
7
int op1 = 12; // 0000 0000 0000 0000 0000 0000 0000 1100
int op2 = 9; // 0000 0000 0000 0000 0000 0000 0000 1001

int andResult = op1 & op2; //8(0000 ... 1000)
int orResult = op1 | op2; //13(0000 ... 1101)
int xorResult = op1 ^ op2; //5(0000 ... 1101)
int notResult = ~op1; // -13(1111 ... 0011)

&(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
2
3
4
5
6
7
int op1 = 12; //0000 ... 0000 1100

int leftShiftResult1 = op1 << 1; //24 (0000 ... 0001 1000)
int leftShiftResult2 = op1 << 2; // 48 (0000 ... 0011 0000)

int rightShiftResult1 = op1 >> 3; //1 (0000 ... 0000 0001)
int rightShiftResult2 = op1 >> 4; //0 (0000 ... 0000 0000)

대입 연산자

좌항에 우항의 값을 저장한다. 기호는 =. 수학의 대입처럼 같다는 의미로 사용되는 것이 아니다.

산술 연산자/비트 연산자와 조합

1
2
3
4
5
6
7
8
9
int num1 = 10;

num1 /= 2; // num1 = num1 / 2; 와 깉음.
num1 += 14; // num1 = num1 + 2; 와 깉음.
num1 *= 2; // num1 = num1 * 2; 와 깉음.
num1 -= 20; // num1 = num1 - 2; 와 깉음.

num1 <<=2; // num1 = num1 << 2; 와 같음.
num1 >>=3; // num1 = num1 >> 3; 와 같음.

References
실무 프로그래밍 입문(C#)