기본 타입 (Primitve Type)
32비트의 정수형 변수를 할당한다면 다음과 같이 코드를 쓸 수 있다.
System.Int32 a = new System.Int32();
그런데 이렇게 쓰면 너무 귀찮으니까... 대부분의 컴파일러는 다음과 같이 표현하도록 해준다.
int a = 0;
그리고 위와 같은 코드는 System.Int32 타입을 사용한 코드와 의미가 동일한 IL 코드를 만들어준다. 이처럼 컴파일러가 직접 지원하는 데이터 타입들을 기본 타입이라고 부른다.
C#의 기본 타입과 FCL에 대응되는 타입은 다음과 같다.
기본 타입 | FCL 타입 | CLS 호환 | 비고 |
sbyte | System.SBtye | ㄴㄴ | |
byte | System.Byte | ㅇㅇ | |
short | System.Int16 | ㅇㅇ | |
ushort | System.UInt16 | ㄴㄴ | |
int | System.Int32 | ㅇㅇ | |
uint | System.UInt32 | ㄴㄴ | |
long | System.Int64 | ㅇㅇ | |
ulong | System.UInt64 | ㄴㄴ | |
char | System.Char | ㅇㅇ | 16비트임, C++에서 쓰는 8비트 아님 |
float | System.Single | ㅇㅇ | |
double | System.Double | ㅇㅇ | |
bool | System.Boolean | ㅇㅇ | |
decimal | System.Decimal | ㅇㅇ | |
string | System.String | ㅇㅇ | |
object | System.Object | ㅇㅇ | |
dynamic | System.Object | ㅇㅇ | FCL타입은 System.Object임에 주목 |
참고
FCL (Framework Class Library) : FCL + CLR 이 닷넷 프레임워크
CLS (Common Language Specification) : 닷넷 컴파일러가 지원해야 하는 표준 규약, 닷넷 프레임웍 언어가 지원해야 하는 언어 규약, 이걸 지키면 닷넷 프레임워크에서 실행 가능하며 CLR을 통해 실행됨.
주의사항
- 운영체제가 32비트 64비트라고 int가 32비트 64비트로 바뀌는 게 아니다. int는 그냥 System.Int32임
- 다른 언어에서는 long이 16비트 혹은 32비트 부호있는 정수인 경우가 있으니 주의해야 함 (C#에서는 64비트)
- FCL이름을 어느정도 알아두는 게 편하다. 가령 System.Convert 타입의 경우 ToSingle 이런 게 있는데 single이 float과 대응된다는 사실을 모르면 좀 혼동스러울 것이다.
- 다른 언어도 CLR을 이용할 수 있는데, FLC의 경우 거의 C#용도로만 짜였기 때문에 Array.GetLongLength같은 경우 사용하는 언어마다 뭘 반환하는지 헷갈릴 수 있다.
따라서
Int32 a = 5;
Int64 b = a;
이런식으로 FCL의 정확한 타입 명칭을 사용하는 것도 고려해볼 만하다.
오버플로우
프로그래밍 언어마다 오버플로우를 처리하는 방법이 다르다. C/C++은 오버플로우를 오류로 생각하지 않는다. 비주얼 베이직 닷넷의 경우 이를 오류로 취급하며 프로그램의 실행을 중단시킨다.
CLR의 경우 기본적으로 오버플로우에 대한 검사를 수행하지 않는다. 예를 들어, CLR의 IL 명령어는 add를 통해 두 값을 더하며 오버플로우에 대한 검사를 수행하지 않는다. 이때, add.ovf 라는 명령을 이용하면 두 값을 더하는 도중 오버플로우가 발생할 경우 System.OverflowException예외를 발생시킨다. 마찬가지로 mul, sub, conv역시 .ovf를 붙여 오버플로우를 검사하게 할 수 있다.
오버플로우에 대한 검사를 수행하지 않으므로써, 프로그램은 좀 더 빠르게 동작한다. 그 대신 개발자들은 오버플로우가 발생하지 않도록 코드를 설계해야 한다.
C#컴파일러가 오버플로우 검사를 수행할 수 있는 IL코드를 만들게 하기 위해서는 컴파일러 스위치 중 /checked+를 지정해야 한다. 이 스위치는 컴파일러가 add, sub, mul, conv 등의 IL 명령을 .ovf명령으로 바꾼다. (대신 프로그램은 조금 느려진다.) 스위치는 다음과 같이 checked/ unchecked 연산자를 통해 설정할 수 있다.
UInt32 a = unchecked((UInt32)(-1)); // 문제 없음
Byte b = 100;
b = checked((Byte) (b + 200)); // System.OverflowException 발생
이 두 연산자는 다음처럼 블록으로도 쓸 수 있다.
checked
{
Byte b = 100;
b += 200;
}
단, 중간에 메서드를 호출하는 경우에는 메서드 내부에서 또 스위치를 지정해주어야 한다.
checked {
Byte a = 200;
Byte b = 200;
Add(a,b); // 메서드 내부에서 checked 안해주면 OverflowException 발생하지 않음
}
오버플로우와 관련하여 고려해볼만한 내용들
- UInt32, UInt64같은 부호 없는 데이터보다는 부호 있는 데이터를 사용하는 것을 고려할만하다. 이렇게 하면 오버플로우나 언더플로우에 대한 검사를 더 잘 수행할 수 있다. 또한 클래스 라이브러리의 여러 부분들은 부호 있는 값을 하드코딩된 형태로 반환하고 있으며 이런 것들을 캐스팅 없이 활용하는 것이 좋다. 그리고 애초에 부호 없는 숫자 타입은 CLS호환 사양이 아니다.
- 명시적으로 checked나 unchecked 블록을 사용하여 오버플로우가 발생할 수 있거나 없는 부분(필요 없는 부분)을 지정해주면 좋다.
- checked나 unchecked 키워드를 사용하지 않는 경우에는 직접 오버플로우에 대한 검사를 수행하고 필요한 경우 예외를 발생하도록 하는게 좋다.
참고로 checked, unchecked 연산자 말고도 IDE에서 일괄적으로 /checked 스위치를 지정해 줄 수 있다.
'언어 > C#' 카테고리의 다른 글
C# 메서드 - 1. 생성자 (0) | 2020.11.22 |
---|---|
C# 의 타입 - 4. 객체의 식별 (Equals, GetHashCode) (1) | 2020.11.14 |
C# 의 타입 - 3. 값 타입과 참조타입 / 박싱, 언박싱 (4) | 2020.11.14 |
C# 의 타입 - 1. 타입의 기초 (0) | 2020.10.31 |
Attribute (0) | 2020.10.17 |