-
[C 언어] 형변환 _ 구조체 형변환뜯고 또 뜯어보는 컴퓨터/씨쁠쁠 C++ 2022. 1. 22. 20:22반응형
0. C 에서 형변환이란?
- C 언어에서 형변환(Type casting)이란 어느 한 데이터 타입에서 다른 데이터 타입으로의 전환입니다. Data conversion 이라고도 하며, C 에서는 빈번한 형변환이 이루어져 type casting 의 의미를 집고 넘어가는게 중요하다고 생각합니다.
- C 언어에서는 형변환을 하기 위해 2가지의 형변환을 제공하는데,
- 묵시적 형변환 (implicit type casting)
- 명시적 형변환 (explicit type casting)
2가지를 제공하고 있습니다. 이에 대해서는 밑부분에 추가 설명 하도록 하겠습니다.
1. 묵시적 형변환 Implicit Type casting
- 묵시적 형변환의 경우, 데이터 형변환이 일어날때, 본질의 의미를 흐리지 않고, 형변환이 이뤄지는 것을 의미한다. 이러한 형변환(implicit type casting) 은 변수에 담겨있는 값의 중요성이 깨지지 않을때, 그 본질이 있다고 할 수 있다. 그럼 어떻게 " 데이터 형변환이 이루어 지는데, 본질의 의미가 안깨지냐고 할 수 있냐 ??? " 라고 물어볼 수 있는데, 이는 lower data type 이 큰 data type 으로 형변환이 이뤄지며, 데이터의 유실 없이 형변환이 수행 된다 할 수 있겠다.
- 모든 데이터 형들은 나름의 계층 구조를 가지고 있는데, 밑의 그림과 같이 제일 하단에 있는 char 과 short 에서 int -> ... -> long -> long double 순으로 계층구조를 가지고 있고, 서로 다른 데이터 구조에 대해연산 [operands] 를 수행한다 했을때, 상위 데이터 구조로 변경이 된다.
- implicit type casting 은 수많은 오류의 원인이 되기도 하므로 항상 꼼꼼히 챙겨줘야 한다. 이러한 type casting 을 막기 위해, C++ 에서는 explicit 키워드를 제공하기도 한다. // double -> float, 큰값에서 작은 형 변환은 complier 에러를 초래하기도 하지만, 항상 2번 확인하는 습관을 기르자..!
#include <stdio.h> int main() { int num = 1; char c = 'k'; /* ASCII 값 -> 107 */ float sum; sum = num + c; /* char과 int가 상위 형태의 float 으로 자동 형변환 */ printf( " sum = %f\n", sum ); /* 108 출력 */ }
2. 명시적 형변환 Explicit Type casting
- 명시적 형변환의 경우, "명시"라는 단어가 붙듯이, 형변환에 대해 바꿔줄 형변환을 미리 명시하는데에 그 의미가 있다. 보통 (type_name) expression 처럼 적어주고 사용하게 된다.
- 예를 들어 우리가 C 에서 가장 많이 접하는 예제로 . float num = (int) 32 / (int)7 을 해주면, complier 에러가 발생하면서, num=4.0 (이때는 implicit type casting 실행)으로 나오게 될 것이다. 이를 해결하기 위해, num=(float)32/ 7 을 해주어야 비로소 num=4.57... 의 값이 나오게 될 것이다. 이처럼 우리가 원하는 형변환으로 미리 명시해주는것을 명시적 형변환이라고 한다.
2-1. 포인터의 형변환
- 포인터의 형변환도 명시적 형변환과 마찬가지로 (*data type) <포인터> 의 형태를 띄게 된다. 밑의 예를 보게 되면, numptr 은 원래 int 를 가르키는 pointer 였지만, (char *) numptr 의 꼴로 char 형태로 바꿔주게 되어, 역참조 하게 되었을때 0x78 을 반환하게 된다. 여기서 "왜 0x12 ... 로 시작하면, 0x12 아니냐" 라고 물으실 수 도 있는데, intel 프로세서를 쓰는 칩 제조사는 "리틀 엔디안"이라는 데이터 값을 저장하는 방식을 쓰기 때문에 실제로 저장되는 순번은 0x78563412 이런식으로 저장되고, IBM 이나 모토로라는 빅엔디안의 방식을 쓰기에, 0x12345678 순으로 저장된다. < 너 무 깊 어 지 니 알 아 두 기 만 하 자.!>
/* [ C 언어 코딩 도장 참고 ] */ #include <stdio.h> #include <stdlib.h> int main() { int *numPtr = malloc(sizeof(int)); // 4바이트만큼 메모리 할당 char *cPtr; *numPtr = 0x12345678; printf("0x%x\n", *(char *)numPtr); // 0x78: numPtr1을 char 포인터로 변환한 뒤 역참조 free(numPtr); // 동적 메모리 해제 return 0; }
- 그럼 실제로 어떻게 프로그래밍적으로 포인터 형변환을 이뤄내는지 쓰임새를 살펴보자. 밑의 프로그래밍적 기법은 socket 함수에서 주소체계를 정의하게 되면, sockaddr_in 을 보통 선언하는데, 실제로 많은 함수에서 sockaddr 로의 포인터 형변환을 많이 실시하기에 예시로 들어봤습니다.
/* 소켓 프로그래밍에서 주소를 전달 할때, sockaddr_in 이라는 구조체를 사용하는데, 다음 과 같은 구조를 띄게 된다. */ struct sockaddr_in { as_family_t sin_family; uint16_t sin_port; // 16비트 PORT 번호 struct in_addr sin_addr; //32 비트 ip 주소 char sin_zero[8]; } /* sockaddr_in 구조체를 생선한다음, 여러가지 형태로 전달이 되게 되는데, 보통은 (struct sockaddr*) <sockaddr_in 포인터> 형식으로 변환을 하게 된다. 이는 sockaddr_in 구조체 포인터를 sockaddr 구조체 포인터로 변환하는 것으로 해석이 되는데, 그럼 sockaddr_in 구조체를 살펴보자. */ struct sockaddr { sa_family_t sin_family; // 위와 동일 char sa_data[14]; // 14 바이트 char 형 배열 } /* 위의 sockaddr_in 에서 sin_family 를 제외한 나머지 부분이 char 형 배열로 포인터 값으로 바뀌게 되었는데, 이는 위의 uint16_t 2바이트, in_addr 4 바이트, sin_zero 8 바이트 총합 14 바이트를 뜻하게 되어, sockaddr_in 구조체가 sockaddr 구조체로의 형변환은 데이터 손실 없이 포인터 형변환이 이뤄지게 된다. */
- 긴 글 읽어주셔서 감사합니다.
반응형'뜯고 또 뜯어보는 컴퓨터 > 씨쁠쁠 C++' 카테고리의 다른 글
Shared_ptr 내부 구조 & 주의점 (0) 2022.10.26 가상 소멸자 / 가상 소멸자 사용 이유 (0) 2022.10.26 [C++] String to Int, Float, Double 자료형 / stoi, stol, stoll (0) 2022.10.26 Map 을 Vector 로 변환하기 (0) 2022.10.20 스마트 포인터 : Unique_ptr (0) 2022.05.08