1. C++ Explicit Casting 개념
#include <iostream>
#include <cstdlib>
int main() {
int* p1 = malloc(sizeof(int)*10);
free(p1);
}
- 위의 코드를 살펴보면 malloc을 사용해 메모리를 할당하고 주소를 int포인터로 받았다.
- 하지만 사실 malloc이 반환하는 데이터 타입은 void포인터이다. 이것을 int포인터에 담을 수 있는가?
- 위의 표에 따라 C++에서는 암시적 변환이 허용되지 않아 컴파일 시 에러가 난다.
- 그렇다면 왜 새로운 캐스팅이 필요할까?
=> C의 캐스팅은 논리적이지 않고, 위험하고, 버그의 가능성이 높다.
2. C언어 방식 캐스팅의 단점
#include <iostream>
int main() {
int n = 0;
//double* p1 = &n; // error
double* p1 = (double*)&n; // ok
*p1 = 3.4; // ?
}
- int타입의 주소를 double타입 포인터 변수 p1에 담고있다. 따라서 컴파일 시 에러가 난다.
- 하지만, 강제로 double포인터로 캐스팅을 하면 문제없이 컴파일이 된다.
- 이 때, *p1에 3.4를 넣으면 어떻게 될까?
- 위 그림은 *p1에 3.4를 넣기 전까지의 코드를 그림으로 표현한 것이다. 여기서 *p1에 3.4를 넣는다면
- 컴파일러가 봤을 땐 *p1이 double을 가리키므로 3.4를 넣는데 문제가 없다고 생각하고, 메모리를 8byte를 잡게 된다.
- 이럴경우 잘못된 메모리 참조로 프로그램이 죽을 수도 있고, 더 넓게 잡힌 공간에 다른 변수가 있다면 전혀 다른 결과가 나올 수 있다.
#include <iostream>
int main() {
const int c = 10;
// int* p2 = &c; // error
int* p2 = (int*)&c; // ok
*p2 = 20;
std::cout << c << std::endl; // 10? 20?
}
- 상수 int 타입 c를 10으로 선언 및 초기화 한다.
- 이 c의 주소를 int포인터 p2에 담으면 p2의 값이 바뀔 수 있어 위험하다. 다행히 이 코드는 컴파일 시 에러가 난다.
- 하지만 캐스팅을 통한 컴파일 오류를 없앨 수 있다. 위에서 c의 값은 10이 나올까? 20이 나올까?
결론
1. 논리적으로 위험한 캐스팅도 아무런 경고 없이 허용한다.
=> 개발자의 의도인지 실수인지를 명확하게 구별할 수 없다.
2. C++언어의 해결책
=> 용도 별로 다른 캐스팅 방법을 사용하자.
=> 용도 별로 4개의 캐스팅 연산자가 제공된다.
3. C++의 4개의 캐스팅 연산자
void move(int) {}
void move(double) {}
int main(void) {
const int c = 10;
double d = 3.4;
int n = static_cast<int>(d);
int* p1 = static_cast<int*>(malloc(100));
// auto p2 = &move; // error
auto p2 = static_cast<void(*)(int)>( &move ); // ok
int* p3 = static_cast<int*>(&d); // error - 포인터끼리의 변환
int* p4 = static_cast<int*>(&c); // error - 상수성 제거
}
- 사용 방법 : cast연산<타입>(표현식); -> ex. static_cast<double>(num);
3.1 여러가지 cast
int main() {
int n = 10;
// double* p1 = &n;
double* p1 = reinterpret_cast<double*>(&n);
// int* p2 = 10;
int* p2 = reinterpret_cast<int*>(10);
// double d = reinterpret_cast<double>(n); - error
}
- reinterpret_cast
=> 서로 다른 타입의 포인터 사이의 변환
=> 정수와 포인터 사이의 변환
int main() {
const int c = 10;
volatile int v = 20;
int n = c;
// int* p1 = &c;
int* p1 = const_cast<int*>(&c);
// int* p2 = &v;
int* p2 = const_cast<int*>(&v);
// double* p3 = const_cast<double*>(&c); - error
}
- const_cast
=> 포인터 변수와 참조 변수 사이의 상수성(const)과 volatile속성을 제거하기 위한 캐스팅
- 예제 : const int형 변수의 주소를 char*변수에 담아보기
int main() {
cont int c = 10;
// char* p = static_cast<char*>(&c);
// char* p = const_cast<char*>(&c);
// char* p = reinterpret_cast<char*>(&c);
char* p = static_cast<char*>(
const_cast<int*>(&c)); // 방법1
char* p = const_cast<char*>(
reinterpret_cast<const char*>(&c)); // 방법2
}
=> static_cast : 상수성을 제거할 수 없고 int를 char*로 바꿀수도 없다.
=> const_cast : 상수성을 제거할 수 있으나 int를 char*로 바꿀순 없다.
=> reinterpret_cast : int를 char*로 바꿀순 있으나 상수성을 제거할 수 없다.
=> const_cast와 reinterpret_cast를 같이 사용한다.
=> 방법1. 상수성을 먼저 제거한 후 타입 변환
=> 방법2. 타입을 먼저 변환(상수성 유지) 후 상수성 제거
=> 방법3. char* p3 = (char*)&c; -> 개발자의 의도가 명확하지 않다.
'프로그래밍 > C++' 카테고리의 다른 글
[C++] 객체지향 프로그래밍의 개념(1) (0) | 2019.10.29 |
---|---|
[C++] 동적 메모리 할당, nullptr (0) | 2019.10.27 |
[C++] 레퍼런스(reference) (0) | 2019.10.22 |
[C++] range for / if init / if constexpr (0) | 2019.10.18 |
[C++] C언어와 다른 C++ 함수(2) (0) | 2019.10.11 |