3 분 소요

  • 타입 크기에 의존하여 코딩하지 마라. OS에 따라, 컴파일러에 따라, 시스템 비트수에 따라 크기가 달라질 수 있다.
  • 대소 비교가 필요한 경우에는 정수 타입을 사용하라. 실수 비교는 오차가 있다.

모던 C++

개요

C++에는 하기의 기본 타입들이 있습니다. 크기가 고정된 것은 float, double 밖에 없습니다. 표준에 의하면 char마저 적어도 1byte입니다. 그냥 1byte가 아닙니다. 대부분 OS에 따라, 컴파일러에 따라, 시스템 비트수에 따라 달라지므로 크기에 의존해서 코딩하면 안됩니다.(특히 intwchar_t요.)

항목 내용 용량
bool true 또는
false
1 <= sizeof (bool) <= sizeof(long)
char 적어도 1byte 문자 대부분 1byte
wchar_t 적어도 2byte 와이드 문자 시스템의 처리 방식에 따라 다르며, 2byte 또는 4byte.
Windows는 2byte
short 적어도 2byte 정수 대부분 2byte
int 적어도 2byte인 기본 연산 단위 크기의 정수 16bit : 2byte,
32bit : 4byte,
64bit : 4byte
unsigned 부호 없는 int int 와 동일
long 적어도 4byte인 int보다 크거나 같은 정수 16bit : 4byte,
32bit : 4byte,
64bit : 8byte
float 단정밀도 부동 소수점 실수 4byte
double 배정밀도 부동 소수점 실수 8byte
long double 확장 정밀도 부동소수점 실수 16byte 또는 10byte 또는 8byte. 단, Visual C++ 은 double 과 동일
* 포인터 32bit : 4byte,
64bit : 8byte
& 참조자 참조하는 개체의 별칭으로서 해당 용량은 스펙에 정의되지 않음.
다만, sizeof()시 참조하는 개체와 동일 크기를 리턴하도록 스펙에 정의됨.(sizeof(T&) == sizeof(T))
enum 열거형 상수 모든 열거자 값을 나타낼 수 있는 정수 형식의 크기. (보통 4byte)

그외 signed, unsigned, short, long 와 결합하여 다양한 조합이 나올 수 있습니다.(https://en.cppreference.com/w/cpp/language/types 참고)

(C++11~) autodecltype()이 추가되어 값으로부터 타입을 추론하며, 코딩이 간편해 졌습니다.
(C++11~) long long 타입이 추가되어 최소 8byte크기를 보장합니다.
(C++11~) char16_t, char32_t 타입이 추가되어UTF-16 인코딩 문자와 UTF-32 인코딩 문자를 지원합니다.
(C++20~) char8_t 타입이 추가되어 UTF-8 인코딩 문자를 지원합니다.

타입 별칭

typedef로 타입의 별칭을 만들 수 있습니다.(절대 매크로 쓰지 마세요!!! 매크로 상수 참고)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 단순하게
typedef unsigned long ulong; 
ulong myVal; // 타입의 별칭으로 정의

// 배열
typedef int MyArray[5]; 
MyArray arr; // int arr[5]; 와 동일 
arr[0] = 10; // 첫번째 요소에 값 대입
EXPECT_TRUE(arr[0] == 10);

// 구조체
typedef struct {int a; int b;} MyData, *pMyData;
MyData myData1;
MyData* myData2;
pMyData myData3; // MyData* 와 같음
myData1.a = 10; // a에 값 대입
EXPECT_TRUE(myData1.a == 10);

// 함수 포인터
typedef int (*Func)(int, int); // 함수 포인터 typedef

int f(int a, int b) {return a + b;} // 함수 정의
Func func = f; // 함수 포인터 저장

// template의 타입 재정의
template<class T> 
struct ClassT { 
    typedef T Type;
    typedef const T ConstType; 
}; 
ClassT<int>::ConstType constVal = 20;

(C++11~) using을 이용한 타입 별칭이 추가되어 typedef 보다 좀 더 직관적인 표현이 가능해 졌습니다.

타입 크기

sizeof()를 이용하면 개체 용량을 byte 단위로 구할 수 있습니다. 포인터의 경우는 32bit 시스템에서는 4byte, 64bit 시스템에서는 8byte이고, 참조자의 경우는 참조하는 개체와 동일 크기를 리턴합니다.

1
2
3
4
5
6
7
8
9
10
11
class MyClass {
    int m_X;
    int m_Y;
};

MyClass myClass;
MyClass& myClassRef = myClass;

EXPECT_TRUE(sizeof(myClass) == 8);
EXPECT_TRUE(sizeof(myClass) == sizeof(myClassRef)); // sizeof() 시 참조하는 개체와 참조자는 크기가 같습니다.

타입 최대/최소값

numeric_limits로 타입의 최대, 최소값을 알 수 있습니다.

1
2
EXPECT_TRUE(std::numeric_limits<int>::max() == 2147483647);
EXPECT_TRUE(std::numeric_limits<int>::min() == -2147483648);

실수 비교

실수는 부동 소수점 형태로 데이터를 저장합니다. 소수점 이하의 정밀도가 상황에 따라 다르며, 1.0을 저장했는데, 1.0000001이 저장되었을 수 있습니다.

그래서 == 나 대소 비교가 부정확합니다. 손실되면 안되는 민감 정보나, 정확한 계산을 필요로 하는 경우에는 정수형을 사용하셔야 합니다.(로지컬 단위 참고)

다음처럼 epsilon()을 이용해 오차 범위를 고려하여 비교할 수는 있지만, 권장하지는 않습니다.

1
2
3
4
5
6
7
8
9
10
11
12
bool Equals(double a, double b) {
    double diff = fabs(a - b); // 두수의 차

    // (△) 비권장. 두수의 차가 오차 범위보다 작으면 같은 수
    return (diff < std::numeric_limits<double>::epsilon()) ? true : false;
}

double a = 10.0;
double b = a;
b += 1.0;
b -= 1.0;
EXPECT_TRUE(Equals(a, b)); 

댓글남기기