본문 바로가기

별걸다하는 IT/프로그래밍언어

[C / C++ / Java ] 묵시적 Implicit (자동형변환), 정수의 승격, 오버플로우 - 형변환 type casting

반응형

[C / C++ 목차 완전 정복]

안녕하세요~! 오늘도 찾아온 블로그 주인장입니다ㅋㅋ

저번 시간에 이어서 변수 관련 포스팅을 이어나가고 있어요. 이전 포스팅 내용을 안다는 가정하에 진행하도록 할게요~!

 

혹시 이전 포스팅이 궁금하신 분은~~~

↓ 프로그래밍에서 변수란?

 

프로그래밍에서 변수(variable)란? 자료형이란? 데이터 타입 종류 및 크기

[C언어, C++언어 완전 정복! 강의 목차 링크] C, C++, C#, Java, Python 등등.. 다양한 언어가 있는데요. 프로그래밍 공통은 이러한 언어들에서 공통적으로!! 나오는 개념을 정리하는 카테고리예요. 공통적으로 다..

jhnyang.tistory.com

↓ 변수 선언, 초기화 방법과 명명 규칙 및 표기법

 

[Java/C/C++] 변수 선언, 초기화 방법과 변수 명명 규칙, 카멜(Camel) 파스칼 표기법

[ Java / C / C++ 프로그래밍 완전정복 목차 ] 안녕하세요~! 오늘도 말랑이몰랑이 블로그를 찾아와주신 방문자님들 반가워요!! 저번 시간에 '변수가 무엇인가'에 대한 포스팅을 진행했었죠! ▼[공통,Java,C,C++등..

jhnyang.tistory.com

 

오늘 주제는..

[ 형변환 Type Casting ~! ]

서론...

저번 시간에 변수란 물건을 보관하기 위해 담고 있는 상자와 같다고 얘기를 했어요!

그리고 그 상자의 정체는 바로 메모리~! 라고 설명을 진행했습니다.

저장하고자 하는 데이터마다 크기가 각기 달라서 메모리를 차지하는 공간의 크기도 다 다르고 이를 알려주는게 '데이터 타입'이라는 내용을 설명했었는데 기억이나나요~? 요 기억을 머릿속에 담은 상태로 오늘 진도를 나가볼게요 ㅎㅎ

 

여러분 만약에 내가 프로그램을 평균을 계산하는 프로그램을 짰어요. 보통 점수는 정수잖아요?

//C++언어
#include <iostream>
using namespace std;
int main()
{
     int kor, eng, math; //점수는 보통 정수니까 타입을 int로 선언해줬어요
     cin>>kor>>eng>>math; //그리고 입력을 받습니다.
     cout<< "세 과목의 평균은: "<< (kor + eng + math)/3 <<"입니다 \n"; //출력하기
     return 0;     
}
//자바의 경우
import java.util.Scanner;
public class Main {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int kor = sc.nextInt(); //입력받기 
		int eng = sc.nextInt();
		int math = sc.nextInt();
		System.out.println("세 과목의 평균은 "+ (kor+eng+math)/3 +"입니다."); //출력하기
	}
}

입력을 60 70 80을 넣어줘볼게요.

음 결과가 잘 나오네요. 근데 만약 각각의 점수를 66 83 92를 넣으면..? 80.33333333...이 나와야 하는데!!! 

80.333...이 아니라 80이 나와버림!

요렇게 나와버립니다. 성적은 예민하잖아요 ㅎㅎ 1점 차이로 등수가 바뀔수도 있는데 특히 수능 같은 경우라던가.. 이런 일이 발생하면 큰일나겠죠.?ㅎㅎ 복잡한 연산으로 날씨를 예측한다던가,,, 인공위성의 궤도를 추적한다던가... 이런 수학적인 프로그램에서는 더더욱 이러한 작은 차이가 큰 오차로 이어질 수 있어요!!

왜 이런 오류가 발생하는거죠??

데이터의 표현 방식이 바뀌었기 때문입니다.

위의 예제의 경우, 정수형과 실수형은 저장 방법이 완전히 다른데 실수를 정수형 저장 방식에다가 꾸겨넣었기 때문에 데이터 손실이 일어난 경우예요. 정수끼리 연산을 하면, 결국 그 정수 표현 방법끼리 연산을 한거기 때문에 결과도 정수 포맷이라 정수가 들어가야 오류가 나지 않거든요 ㅎㅎ

 

조금만 생각해보면, 이전 포스팅에서 분명 int타입은 4바이트라고 했는데 실수인 float도 메모리에서 차지하는 크기가 4바이트예요!!

똑같은 4바이트 크기의 이진수를 가지고 수를 표현하는데, 두 타입이 표현하는 범위는 전혀 다릅니다~! int는 '-2,147,483,648 ~ +2,147,483,647' 범위를 표현할 수 있는데, float은 ±3.4×10−37 ~ ±3.4×10+38 ​만큼 표현할 수 있으니까요 ㅎㅎ

어떻게 같은 공간인데 표현할 수 있는 수의 범위가 다르지? 왜냐! 수를 표현하기 위해 정한 규칙과 계산법이 서로 상이하기 때문입니다!!

--> 요거는 나중에 논리회로 과목에서 부동소수점 표현방법에 대해 배울때 디테일하게 알게 될거예요 ㅎㅎ (부동 소수점 표현 방법을 추가적으로 찾아보면서 공부하셔도 되고 일단은 이렇게 이해를 하고 넘어가도 됩니다 )

형변환이란? Type casting

이런 사건을 방지하기 위해, 형변환이라는 것이 있습니다.

쉽게 말하면, 데이터 타입을 변경해 타입을 일치시켜주는 거예요. ㅎㅎ

 

형변환 방법: (타입) 피연산자

형변환 연산자: 괄호 ( )

float avg = (float) (kor + eng + math)/3;
//결과 값이 정수로 표현되는게 싫어서~! 
//좀 더 자세하게 실수로 성적을 뽑고자 괄호 안에 float을 적어 타입을 실수로 변경해주었음!

형변환은 요렇게~ 괄호안에 바꾸고 싶은 타입을 적어서 변경해줍니다.

데이터 타입을 변경하는 걸 casting이라고 하는데, 크게 묵시적 형변환, 명시적 형변환 두 가지로 나눌 수 있습니다.  

묵시적 형변환 (Implicit type casting)

작은 데이터가 담겨 있는 상자에서 큰 상자로 옮기는 작업입니다. 그래서 데이터 손실이 일어나지 않아요 ㅎㅎ

1바이트에 담겨 있는 33이라는 데이터를, 2바이트를 차지하는 short이라는 타입의 상자로 옮겨줘도 값은 똑같겠죠? 남는 공간에는 그냥 뭐 0을 채워넣어 버리면 되니까요~!

 

원래 타입 변환은 형변환 연산자를 이용해서 해줘야 하지만, 요럴 경우 데이터 손실이 일어나지 않으므로 걍 편의상 '( )'연산자를 생략할 수 있게해줬습니다. 왼쪽 오른쪽 자료형이 일치하지 않는데 형변환 연산자(괄호 열고 닫는 '()'요거) 가 없을 경우, 왼쪽을 기준으로 형변환이 내부적으로 알아서 일어납니다.

자동으로 타입을 바꿔준다라고 해서 자동형변환 (automatic conversion) 이라고도 불려요 ㅎㅎ

요렇게~~ 작은 사이즈 상자에 있던 데이터를 큰 상자로 옮길 경우만 해당돼요ㅎㅎ

확인해볼까요~?

작은크기 타입 --> 큰 타입

sizeof를 이용하면 자료형의 크기를 확인할 수 있어요 ㅎㅎ (C, C++)

자바에는 sizeof 함수가 읍습니다ㅠ

//C++언어
#include <iostream>
using namespace std;
int main()
{
	short smallnum = 10;
	cout << "short type: " << sizeof(smallnum) << endl; //반환되는 smallnum의 크기 출력 
	int bignum = smallnum; //short 크기를 갖는 smallnum이 int타입으로 들어갔어요!
	cout << "int type: " << sizeof(bignum) << endl;
	return 0;
}

결과:

short type: 2

int type: 4

int bignum = (int) smallnum;

이렇게 해주지 않았는데, 자동으로 타입이 변환된 것을 확인할 수 있어요~!

큰 타입 --> 작은 타입

그럼 반대로, 형변환 연산자 없이, 큰 타입을 작은 타입에다가 우겨넣으려고 하면 어떻게 되는지 볼게요~

자바 예시

이클립스의 경우 'cannot covert from float to int' 에러가 뜹니다. 큰 값을 작은 상자에 꾸겨넣지 말라는거죠 ㅎㅎ

비주얼스튜디오의 경우, 데이터의 손실이 일어나도 허용해주긴 합니다. 어디까지 허용하느냐는 IDE마다 차이가 있어요 ㅎㅎ 다만 자동형변환의 경우 이렇게 데이터의 손실이 일어날 수 있으니, 왠만하면 기호를 생략하는 것보다는 일부로 명시해주는 것이 평소 습관에 좋다~ !

자동형변환은 연산자가 없다 해도 안일어나는게 아니라, 실질적으로 형변환 연산자를 사용하는 거처럼 컴파일러가 처리해주는거다~!

 

타입을 변환한다는 것은, 차지하는 메모리의 크기를 변경시키는 것도 있지만, 정수 <-> 실수 변환처럼 표현법을 변경시키는 것도 해당됩니다.

자바 예시

float 값을 정수로 변환시키려고 하면 역시 에러~~

뭐 쓰다보니 말을 이러쿵 저러쿵 복잡하게 했는데 어렵게 생각할 필요 없이, '자료형이 다르면 맞춰주자' 생각하면 됩니다.

오버플로우 OVERFLOW

위의 예시처럼 작은 상자 한 개를 큰 상자에 옮기는건 사실 큰 문제가 발생되지 않아요 ㅎㅎ

4인승 차를 몰고다니던 4인 가족이 차를 8인승으로 바꿨다고 해서 문제가 되는건 아니니까요~! 다만 8인승이여도.. 갑자기 여행가는데 두 가족이 합세하게 되면,, 탑승할 자리가 없어지겠죠?

즉 주의해야 할 때는 사칙연산처럼 무언가 연산을 할 때입니다ㅎㅎㅎ

 

예시를 볼게요

참고로 자바에서는 객체 특성을 이용해서, Short.MIN_VALUE (타입.MIN_VALUE) 또는 Short.MAX_VALUE(타입.MAX_VALUE) 로 자료형이 가질 수 있는 수의 범위를 구할 수 있습니다.

//자바 
public class Main {
	public static void main(String[] args) {
		System.out.println("short type: "+Short.MIN_VALUE+" ~ "+Short.MAX_VALUE);
		short a = 1000, b = 32767;
		short result = a + b;
		System.out.printf("result=%d\n", result);
	}
}
//C++언어 short범위는 -32768 ~ 32767
#include <stdio.h>
int main()
{
	short a = 1000, b = 32767;
	short result = a + b;
	printf("result=%d\n", result);
	return 0;
}

short는 2바이트로 가질 수 있는 최대값이 32767입니다.

32767 데이터를 가지고 있는 short형 변수 b와 똑같은 자료형을 가진 변수 a를 더해줘봤어요 ㅎㅎ

(즉 short+short)

어차피 연산하는 변수들 자료형이 short니까 ~~ 결과 값을 저장할 변수 result도 short 타입으로 만들어주고 실행해봤습니다.

(short = short + short)

타입을 맞춰줬는데,, 결과가 -31769가 나왔어요 32767+1000이면 33767이 나와야하는데~~ 대체 왜 마이너스가 나온거지 이게 뭔가요~~ ㅎㅎ

 

이클립스의 경우, 컴파일에서부터

아예 에러가 떠버리네요! ㅎㅎㅎ

대체 왜 마이너스 값이 나왔을까?

short의 경우, 가장 상위비트가 0이면 양수를, 1이면 음수를 의미합니다.

그래서 가장 맨 앞 첫 비트는 부호를 판단하고 나머지 비트로 실제 값을 나타내요.

요렇게 맨 앞에는 부호 비트라 건들면 안되는데,,, 뒤에 연산들이 더해지면서 1씩 앞 비트로 올려보내고, 그 결과 침범하면 안되는 첫 번째 비트 부분까지 침범해버리는 이런 상황을 오버플로우 됐다고 합니다.

양수 더하기 양수가 음수가 나와버렸자나요! ㅎㅎ (오버플로우와 언더플로우에 대한 포스팅은 나중에 또 상세히 빼보도록 할게요 얘네도 뭐가 정리할게 많아서리...ㅎㅎ)

'short가 표현할 수 있는 최대 크기인 32767에 어떤 양수(여기선 1000)를 더했더니 short 범위를 벗어나게 되는 현상이 일어났고 그 결과로 값이 음수가 나오게 된 것이다 그리고 이걸 오버플로우라고 한다' 라는 것을 정리해주고 싶었어요 ㅎㅎ 

 

그런데 사실 int보다 작은 값들을 연산할 때에는 컴파일러가 내부적으로 자료형을 int로 변환한 후 연산을 수행해줍니다. (즉 자동형변환이 일어나요)

이클립스가 'cannot convert from int to short'라고 에러가 뜬 것도 a+b 이부분의 자료형이 자동형변환으로 인해 int가 되었는데, 그거보다 작은 short형을 가지는 변수 result에 넣으려고 했기 때문이예요!!

 

그래서 사실 아래 코드는 형변환이 두 번 진행된거예요 ㅎ

short result = a + b;

1. a+b 덧셈연산수행하면서 a는 short, b도 short지만 결국 int로 컴파일러가 자동형변환을 해서 연산하기 때문에~~ 여기서 형변환이 한 번 일어납니다.

2. 그리고 int 값을 좌측의 short 상자에 넣으려고 시도하는 두 번째 형변환!! ㅎㅎ 큰 걸 작은거에 넣으려고 하니까.. 에러뜬거죠 ㅎㅎ

 

이러한 특성 때문에 오버플로우를 조심해야 해요. ㅎ 항상 큰 값일 경우 값의 범위를 신경써줘야 하고, INT로 변형되는 것을 인지하고 있어서 반환 타입도 꾸겨넣지 않게 되게 조심!!

 

그런데 왜 컴파일러는, int 보다 작은 타입에서 연산을 진행할 경우 굳이 int로 자동형변환을 해줬을까요?

정수의 승격 (Integral Promotion)

아래 예제를 볼까요?

//C++언어 
#include <iostream>
using namespace std;
int main(void)
{
	short num1 = 5, num2 = 10;
	cout << sizeof(num1 + num2) << endl;
	return 0;
}

분명 short끼리 계산을 했고, 오버플로우도 아닌데... 결과 값이 4가 나왔어요!!

short는 2바이트인데 왜 4바이트가 나왔을까요?

 

★ 크기가 4바이트보다 작은 자료형 (byte, short 등)의 값을 계산할 때에는 4바이트로 변환하여 연산이 수행됩니다.

왜? int가 CPU가 처리하기에 가장 적합한 크기이기 때문이예요.

좀 더 디테일하게 말하자면, CPU의 word사이즈가 int 사이즈입니다. 쪼끔 더어.. 자세히 말하면.. 워드는 메모리 하나의 번지에서 저장되는 데이터 단위예요 ㅎㅎ

그래서 사실 아주 예~~전 32bit 주소체계 컴퓨터 사용할 때 썼던 int 사이즈는 현재 사용하고 있는 int 크기와는 달라요 ㅎㅎ(메모리 읽어들이는 크기가 달라졌으니까~ ㅎㅎ)

메모리를 short나 byte단위로 계산하고 관리할 수도 있겠지만, 이렇게 하게 되면 하나의 값을 메모리에 올리기 위해서 두 번 세 번 읽어야 하는 부가적인 행동을 함으로써 비효율적이 됩니다. 그래서 차라리 조금 빈 값을 두더라도 int에 맞춰서 수행하는게 효율적이다 라는 거예요 ㅎㅎ

 

결론! 메모리를 아끼려고 short를 쓰고 byte를 써도 결국 int로 변환해서 연산하는 과정을 거치니 그냥 int를 사용하는게 더 효율적이다.


앗,, 저렇게 알고 있었는데, 윤성우의 열혈 C프로그래밍 책을 참조해보니 아래처럼 나와있네요 ㅎㅎ 참고하면 좋을 것 같습니다. (127page)

 

"정말로 int형 연산이 short형 연산보다 빠른가요?

int형 연산이 short형 연산보다 정말 빠른지, 그리고 빠르다면 그 이유는 무엇인지 궁금할 것이다. 사실 오늘날 컴퓨팅 환경에서 정수형 데이터의 연산속도는 정수의 크기에 상관없이 거의 동일하다. int형 연산이 short형 연산보다 빠른 이유는 CPU의 구조적 측면에서 생각해봐야 하는데, 이미 CPU의 성능 및 구조가 이전과는 달리 많이 개선되어서, 정수형 데이터의 연산속도는 사실상 차이가 나지 않는다. 하지만 세상에는 많은 종류의 CPU가 있고, 또 새로운 CPU의 등장도 고려해야 하기 때문에 C언어가 정의하는 정수의 승격은 의미를 갖는다." 


오늘 포스팅에서 배운 ~ 묵시적 형변환 요약 정리

●데이터 자료형의 형 변환이 자동으로 이루이지는 것을 의미한다.

●자동형변환이라고도 함.

●자료형의 크기가 작은 자료형이 크기가 큰 자료형으로 자동형변환되는거~

●C언어의 경우, 데이터 형을 모를 때는 sizeof()함수를 사용하면, 데이터타입의 크기나 변수가 사용하는 메모리 크기를 알 수 있음!

●자바의 경우 데이터형의 범위를 <데이터형.MIN_VALUE>, <데이터형.MAX_VALUE>로 쉽게 확인할 수 있다.

●컴파일러에 의해 수행된다.

●정수와 실수를 연산할 경우 정수가 실수로 자동 형 변환

●int보다 크기가 작은 자료형은 연산 시 int로 자동 형변환

●수식 연산이 이루어질 경우 int보다 작은 데이터타입은 정수의 승격이 발생한다.

●signed 와 unsigned를 연산하면 signed가 unsigned로 자동 형변환 된다.

(걍 읽고 그렇구나~ 인식만 해두세욤 ㅎㅎ)

오늘은 여기까지입니다~! 원래 명시적 형변환과 묵시적 형변환을 같이 포스팅 하려고 했는데...

역시나 쓰다보니 길어지네요 ㅠㅠㅠㅠ

다음 포스팅에서는 명시적 형변환에 대해서 들고 올게요 ~!! ㅎㅎ ㄷㄷㄷ시간이 벌써.ㄷ.ㄷㄷ ㅎㅎ

오늘도 도움되었다면, 공감 댓글 광고보답 감사드립니다 ! 잘못된 정보나 오타가 있을 시 꼭 지적 부탁드려요 질의 환영~ :)

반응형
  • c부터 배우자 2021.08.30 13:32

    정수랑 실수 연산할때 자동 형변환 안되지 않나요? 평균 예제에서 소수점 날라간거처럼