IT/C

C언어 형변환

오오렌지 2023. 1. 2. 14:59

C언어에서 자료형은 메모리에 값을 읽거나 쓸 때 발생할 수 있는 개발자의 실수를 막기 위한 기능도 가지고 있다.

 

즉, 컴파일러는 사용 크기나 형식이 다른 두 메모리가 값을 주고받으면 잠재적으로 문제가 있다고 판단하여 경고 또는 오류처리한다.

 

 

 

예를 들어, 아래와 같이 4 byte 크기의 변수 a값을 1바이트 크기 변수 b에 대입하는것은 문제가 있다고 생각하는것임. 

int a = 50;
char b;
b = a;

위의 코드가 반드시 경고, 오류가 발생하는것은 아님

 

컴파일러의 설정이나 컴파일러의 종류에 따라서는 위 코드가 오류나 경고 없이 처리되기도 한다

 

개발자입장에서, 의도적으로 이걸 맞게 처리하고싶은 경우, 형변환 연산자를 사용하여 컴파일러에게 개발자의 의도를 정확하게 명시하면 경고, 또는 오류처리되지않는다.

 

우리는 이것을 ' 명시적 형 변환 ' 이라고 말 한다.

int a = 50;
char b;
b = (char)a; // 개발자의 명시적 형 변환 사용 -> 경고 또는 오류처리 되지않는다.

 

 


이제 형변환의 종류에 대해 알아보자

 

1. 일반 변수와 상수 간의 형 변환 

 

자료형은 변수에만 적용된다고 생각하지만, 상수도 자료형을 가지고 있다.

 

예를 들어, 아래와 같이 적은 정수형태의 숫자상수는 int자료형으로 처리된다. 

5, 300000, 050, 0x2f

아래와 같이 실수형태로 적은 숫자상수는 double자료형으로 처리된다.

5.0, 1.32e-5

 

따라서, 아래와 같은 상황은 경고나 오류처리

char c;
float f;
c = 1000;
f = 3.141592;

 

왜냐하면, 1000은 int형이고, 3.141592는 double형으로, c 와 f 각각에 대입되는 변수와 자료형이 맞지 않아 경고가 발생하게되는것이다.

그러나, 매번 경고처리가 뜨는것은 아니다. 

 

int형이여도, 아래와 같이 char형의 범위 안에 속하면 경고처리 되지 않는다.

char c;
c = 10;

 

그러나, 실수의 경우 유효 자릿수로 값을 표현하기 때문에 아래와 같이 크기를 작게 적어도 경고는 계속 발생하기때문에 경고를 제거하려면 형 변환 연산자를 사용해야한다.

float f;
f = 3.14;   // 경고발생
f = (float)3.14;  //정상적으로 처리

 

그래서 실수의 경우 위와 같이 형변환을 사용하는것이 불편하기 때문에 아래와 같이 f를 붙여서 형변환 대신 사용이 가능하다.

 float f;
 f = 3.14f;  // 경고 발생 안함

 

 

2. 변수와 변수간의 형 변환

 

서로 다른 크기나 형식이 다른 변수가 값을 주고받는 상황은 잠재적으로 경고 또는 오류상황이다.

따라서, 아래와 같은 상황은 경고 또는 오류처리 될 수 있다. 

 

그런데, int자료형을 char 자료형에 넣는 경우 현재 컴파일러의 기본 설정값으로는 오류처리 되지 않는다. 

 

char c;
int i = 30000 ;
float f;
double d = 3.141592;

c = i;   // 4바이트에서 1바이트로 줄어듦 손실 경고해야하지만 경고안함
f = d;   // 8바이트 실수에서 4바이트 실수로 줄어듦 (손실경고)
i = d;   // 실수에서 정수로 형식이 바뀜 (손실경고)
i = f;   // 실수에서 정수로 형식이 바뀜 (손실경고)

f = i;   // 정수에서 실수로 형식이 바뀜 (손실경고) 
            // 실수가 정수의 범위를 포함하지만 float은 int와 크기가 동일한 자료형이라서
            // int의 정수범위를 float는 포함하지 못함
            
d = i;   //정수에서 실수로 형식이 바뀌지만, double 자료형 범위에 포함되어 경고 안 함

 

실수가 정수가 될 때는 소숫점 이하의 값이 사라지게됨 -> 손실

 

그러나, 실수의 범위가 정수의 범위를 포함하기 때문에 경고가 발생하지 않기도 함

같은 크기의 자료형인 int와 float은 float 자료형에서 정수부가 int크기를 포함하지 못하기 때문에 경고처리되지만,

8byte크기인 double자료형의 경우에는 정수부가 int크기를 포함하기때문에 경고 처리되지 않는다. 

 

형 변환 연산자를 사용하는 규칙

컴파일러 종류나 설정에 따라 간단한 경고상황은 무시하는 경우도 있음

그러나 이런것때문에 버그가 생겨 고생하니까 그냥 형변환 연산자를 사용해서 확실하게 해주는것이 더 좋다

 

형변환 연산자를 사용해야하는 상황

 

형 변환은 연산자를 기준으로 양쪽에 있는 자료형이 다르면 사용해야 한다.

int num = 3000;
int temp;

temp = (int)(int)num;   // 첫번째 int는 temp변수의 자료형, 두번째 int는 num의 자료형 
temp = num;   // 생략가능

두개의 자료형이 일치하기 떄문에 둘 다 생략할 수 있다.

 

그러나 서로 다른 자료형이 대입된다면 ?

char 형 변환 연산자는 생략하지 못하고, 아래의 코드처럼 int자료형만 생략할 수 있다. 

int num = 3000;
char c;

c = (char)(int)num;
c = (char)num;   //int형은 생략가능

 

3. 포인터 변수 간의 형 변환 

 

아래의 그림처럼 동일한 형식의 포인터간에 주소를 대입하는 경우 자료형이 동일함

따라서, 두 형변환 연산자는 모두 생략이 가능하다. 

 

int *p_num;
int *p_temp;

p_temp = (int*)(int*)p_num;

 

 

그러나, 아래와 같이 두 포인터 변수의 자료형이 일치하지 않는다면 위에서 했던 것 처럼 p_temp 변수의 자료형에 해당하는 형 변환 연산자는 그대로 두면 됨

int *p_num;
char *p_temp;

p_temp = (char *) p_num;

 

 

그리고 *를 사용하게 되면 포인터 변수의 자료형이 아닌 두 포인터 변수가 가리키는 대상의 자료형을 의미하기 때문에 포인터 변수의 자료형에서 *가 하나 줄어든다고 생각하면 됨

 

예를 들어, 아래와 같이 선언된 3단계 포인터 p가 있다면, p의 자료형은 int *** 이지만 

*p는 포인터 p가 가리키는 대상이 되기 떄문에 포인터 변수 p의 자료형에서 *가 하나 줄어든 int **가 된다는 의미임

 

int ***p;
*p = ???;     //  *p연산의 자료형은 int **
**p = ???;    //  **p연산의 자료형은 int *
***p = ???;   //  ***p 연산의 자료형은 int

따라서, 아래와 같은 대입 연산에서 포인터 변수가 사용되었다면,char ** 와 int ** 가 아닌 char * 와 int * 이라는 뜻임 

 

int **p_num;
char **p_temp;

*p_temp = (char *)(int *) *p_num;
*p_temp = (char *)*p_num;   //  (int *) 생략해도됨

 

일반 정숫값을 포인터변수에 대입하는 상황을 적어보자

int *p_temp;

p_temp = (int *)(int)1000;
P_temp = (int *)1000;   //  (int) 생략 가능

 

변수의 주소를 얻기 위해, &를 사용하면 결과값이 주소가 나오기 떄문에, 이 값을 포인터 변수에 저장해야 한다.

따라서, &연산자를 사용한 변수의 자료형을 가리키는 포인터 형식과 동일한 자료형이 적용된다.

 

예를 들어, int 형식의 변수에 &연산자를 사용하면 결과값의 자료형은 int *가 되고, int *변수에 &연산자를 사용하면 결과값은 int ** 자료형을 가지게 된다는 의미

 

아래의 코드처럼 int형 변수 num 의 주소를 int * 변수에 저장하는 상황

int num;
int *p_temp;

p_temp =(int *)(int *) #

따라서, 위의 코드는 두 자료형이 동일하기떄문에 둘 다 생략해서 아래와 같이 적을 수 있다.

int num;
int *p_temp;

p_temp = #

만약 int변수의 주솟값을 short * 자료형을 사용하는 포인터에 대입했다면 아래와 같이 자료형이 서로 달라지게됨

int num;
short *p_temp;

p_temp = (short *)(int* )#
p_temp = (short *)#

 

 

참고블로그 형 변환 연산자 사용 규칙 : 네이버 블로그 (naver.com)