4. 값(value) 형식
C#에서 값 형식에는 구조체와 열거형이 있으며 암시적으로 System.ValueType에서 파생된 형식입니다. 형식개요에서 살펴본 바와 같이 값 형식은 각각의 변수가 독립적인 값을 갖게 되며 하나의 변수에서 다른 변수에 대입하였을 때 값을 복사합니다.
4.1 구조체
C#에서는 int 형식이나 bool 형식과 같은 모든 단순 형식들도 구조체입니다. 그리고 다른 많은 언어처럼 여러 멤버를 갖는 사용자 정의 구조체를 만들 수 있습니다.
4.1.1 정수
C#에서는 표현할 수의 범위에 따라 여러 종류의 정수 형식을 제공하고 있습니다.
대부분 키워드가 C언어나 C++언어와 같고 할당되는 메모리 크기와 표현 범위가 비슷합니다. 이러한 이유로 무의식적으로 C#에서도 할당되는 메모리 크기나 표현 범위가 같다고 생각하면서 프로그래밍할 수 있고 이 때문에 논리적 오류를 갖게 될 수 있습니다. C#에서 char는 C언어와 C++언어와 다르게 2바이트가 할당되며 유니코드로 표현할 수 있습니다. 예를 들어, C언어에서는 ASCII 코드로 표현하기 때문에 한글의 한 글자를 하나의 문자로 표현하지 못하고 문자열로 표현해야 했지만, C#에서는 유니코드로 표현하므로 하나의 문자로 표현할 수 있습니다. 그리고 long 형식은 C언어에서 4바이트였지만 C#에서는 8바이트(64비트)를 할당받아 int 형식보다 넓은 범위의 수를 표현할 수 있습니다.
형식 |
메모리 크기 |
표현 범위 |
sbyte |
1바이트 |
-128~127 |
byte |
1바이트 |
0~255 |
char |
2바이트 |
0~65535(유니코드) |
short |
2바이트 |
-32768~32767 |
ushort |
2바이트 |
0~65335 |
int |
4바이트 |
-2147483648~2147483647 |
uint |
4바이트 |
0~4294967295 |
long |
8바이트 |
-9223372036854775808~9223373036854775807 |
ulong |
8바이트 |
0 ~ 18446744073709551615 |
int 와 같은 기본 형식이 구조체라는 사실은 변수나 형식을 통해 보관된 값 이외에도 사용할 수 있는 멤버들이 있다는 것에서 확인할 수 있습니다.
int max = int.MaxValue; int min = int.MinValue; Console.WriteLine(max.ToString()); Console.WriteLine(min.ToString()); |
일반적으로 가장 많이 사용하게 될 멤버로는 최종 사용자가 입력한 값을 기본 형식으로 변환할 때 사용하는 정적 멤버 메서드인 Parse와 TryParse입니다. Parse와 TryParse 메서드는 사용하는 목적에 따라 입력 인자가 다르게 중복 정의(overload)되어 있습니다. 다음은 중복 정의되어 있는 목록 중에 하나입니다.
public static int Parse (string s); public static bool TryParse (string s,out int result); |
Parse 메서드는 문자열을 입력인자로 받아 문자열을 정수로 변환하여 반환합니다. 만약, 변환할 수 없는 문자가 포함되어 있을 때는 예외를 발생합니다. TryParse 메서드는 첫 번째 입력 인자로 전달된 문자열을 정수로 변환하여 output 전달 형식의 두 번째 매개 변수에 대입해 줍니다. 만약, 정상적으로 변환되면 true를 반환하고 변환할 수 없을 때는 false를 반환합니다.
int i1 = int.Parse("-123"); Console.WriteLine(i1.ToString());
int i2 = int.Parse("123"); Console.WriteLine(i2.ToString());
//int i3 = int.Parse("a-123"); 변환할 수 없는 문자가 있기 때문에 예외가 발생합니다. //Console.WriteLine(i3.ToString());
//int i4 = int.Parse("123a"); 변환할 수 없는 문자가 있기 때문에 예외가 발생합니다. //Console.WriteLine(i4.ToString()); |
만약, 다음과 같이 최종 사용자로부터 수를 입력받는 구문이 있다면 변환할 수 없는 문자가 포함된 문자열을 입력하면 예외가 발생합니다. 개발자가 테스트할 때 잘못 입력할 때를 생각하지 못하면 버그가 있는 프로그램을 만들게 됩니다.
Console.WriteLine("수를 입력하세요."); int num = int.Parse(Console.ReadLine()); Console.WriteLine("입력한 수는 {0}입니다.", num); |
이럴 때 개발자는 Parse 메서드를 대신하여 TryParse 메서드를 이용할 수 있습니다.
Console.WriteLine("수를 입력하세요."); int num; if (int.TryParse(Console.ReadLine(), out num)) { Console.WriteLine("입력한 수는 {0}입니다.", num); } else { Console.WriteLine("잘못된 수를 입력하였습니다.."); } |
char 형식은 문자 리터럴 상수 표현을 이용할 수 있습니다. 이는 C언어나 C++언어와 마찬가지로 표현할 문자를 콤마로 감싸 표현하는 것입니다. 앞에서 얘기했듯이 C#언어는 유니코드로 표현하므로 한글도 하나의 문자로 표현할 수 있습니다.
char c = '가'; Console.WriteLine(c.ToString()); |
그리고 char 형식은 묵시적으로 정수 형식과 형식 변환을 제공하고 있습니다.
int cval = c; Console.WriteLine(cval.ToString()); |
4.1.2 부동 소수점 형식
C#에서는 실수를 표현하기 위해 float과 double 형식을 제공하고 있습니다. 그런데 실수는 0.1에서 0.2 사이에도 무한개의 실수가 존재하기 때문에 컴퓨터 프로그램에서는 정확한 수치를 저장하지 못하고 근사치를 보관합니다.
형식 |
메모리 크기 |
표현 범위 |
float |
4바이트 |
±1.5e−45 ~ ±3.4e38 |
double |
8바이트 |
±5.0e−324 ~ ±1.7e308 |
예를 들어, 0.0으로 초기화된 변수에 0.1씩 더해나가면 10번 반복했을 때 1.0이 될 것으로 생각할 수 있습니다. 하지만, 정확한 수치가 아닌 근사치이기 때문에 다음의 코드를 수행하면 반복문을 탈출하지 못합니다.
double d = 0.0; while (d != 1.0) //무한 루프 - 0.1을 정확히 표현하지 못하기 때문에 1.0이 되지 못함 { Console.WriteLine(d.ToString()); d = d + 0.1; } |
4.1.3 decimal
C#에서는 10진수 표현에 적합한 decimal 형식을 제공하고 있습니다. decimal도 근사치를 표현하지만 오차 범위내에서 10진수로 실수 표현을 할 때 효과적으로 프로그래밍할 수 있습니다. double 형식을 사용했을 때 무한루프에 빠졌던 예제 코드를 decimal 형식으로 바꾸면 원하는 결과를 얻을 수 있음을 알 수 있습니다.
decimal d = 0.0m; while (d != 1.0m) { Console.WriteLine(d.ToString()); d = d + 0.1m; } |
10진수로 오차 범위(96비트 정수, 소수 자리수 10의 28)내에서 표현하면 부동 소수점 표현보다 정확하게 표현할 수 있습니다.
또한, C#에서는 수를 표현하는 다양한 형식들에 대해 표현 범위가 좁은 형식을 범위가 넓은 형식으로 암시적 형식 변환을 대부분 지원합니다.
형식 |
변환 형식 |
sbyte |
short, int, long, float, double, decimal |
byte |
short, ushort, int, uint, long, ulong, float, double, decimal |
char |
ushort, int, uint, long, ulong, float, double, decimal |
short |
int, long, float, double, decimal |
ushort |
int, uint, long, ulong, float, double, decimal |
int |
long, float, double, decimal |
uint |
long, ulong, float, double, decimal |
long |
float, double, decimal |
ulong |
float, double, decimal |
float |
double |
4.1.4 bool
C#에서는 true와 false를 값으로 표현할 수 있는 bool 형식을 제공하고 있습니다.
4.1.5 사용자 정의 구조체
C#에서는 사용자에 의해 프로그램에 필요한 형식을 정의할 수 있게 구조체와 열거형 및 클래스를 지원하고 있습니다. 특히, 구조체와 클래스는 여러 개의 멤버들을 하나의 형식으로 캡슐화하여 목적에 맞게 정의할 수 있으며 많은 부분에서 비슷한 문법을 제공합니다. 이 책에서는 구조체에 대한 문법 사항을 상세하게 얘기하지 않고 클래스와 공통적인 문법 사항을 OOP의 특징별로 설명하고 있습니다.
구조체를 정의하여 사용하는 방법은 클래스와 함께 다음 장부터 자세히 다루겠습니다.
4.2 열거형
열거형은 enum 키워드를 사용하여 상수 집합 목록을 열거자 목록에 선언하여 사용될 수 있는 값의 종류를 사용자가 정의하는 형식입니다. 열거형을 정의할 때 나열할 수 있는 상수 집합 목록의 요소는 기본적으로 정수(int) 형식입니다.
enum Season{ NonSeason, Spring, Summer, Autumn, Winter } |
위의 코드는 계절을 Season 이름의 열거형을 정의한 예입니다. 이 경우에 NonSeason부터 차례대로 0, 1, 2, 3, 4에 해당하는 값으로 정의됩니다. NonSeason은 Season 형식의 기본값으로 사용할 수 있게 선언한 것으로 반드시 있을 필요는 없지만 적절한 값이 아님을 나타내기 위해 선언하였습니다. MSDN에서는 될 수 있으면 0값을 사용하지 말거나 적절한 값이 아님을 나타내기 위한 값을 선언하여 디폴트 값으로 사용하라고 권고하고 있습니다.
▶ 열거형 사용 예
class Program { enum Season { NonSeason, Spring, Summer, Autumn, Winter }; static void Main(string[] args) { Season season = Season.NonSeason; Console.WriteLine("계절을 입력하세요. 봄:1 여름:2 가을:3 겨울:4 "); season = (Season)int.Parse(Console.ReadLine()); Console.WriteLine("입력한 계절은 {0}입니다.", season); } } |
▶ 실행 결과 계절을 입력하세요. 봄:1 여름:2 가을:3 겨울:4 2 입력한 계절은 Summer입니다. |
그리고 개발자는 열거형 형식에 선언한 각 열거 목록의 값을 명시적으로 지정할 수 있습니다. 목록에 명시되지 않은 것은 앞 목록의 값+1이 됩니다.
enum BaseScore { MinScore, Bad = 60, SoSo = 75, Good = 90, HighScore = 100 } |
C#에서의 열거형에 선언된 목록의 기본값은 int 형식을 기반으로 하지만 다른 정수 형식으로 정의할 수도 있습니다. 참고로 char 형식은 불가능합니다.
enum Denomination: long { Kilo = 1000, Mega = 1000000, Giga = 1000000000, Tera = 1000000000000, Peta = 1000000000000000 } |
그리고 C#에서는 비트 연산을 통해 여러 열거 목록을 표현하기 쉬울 때 Flags Attribute를 명시하여 열거형을 정의하는 방법을 지원합니다.
[Flags] enum MyFlag { HasCar = 0x01, Married = 0x02, HasHouse = 0x04 } class Program { static void Main(string[] args) { MyFlag flag = MyFlag.HasCar | MyFlag.Married | MyFlag.HasHouse; Console.WriteLine("{0}", flag); } } |
▶ 결과 HasCar, Married, HasHouse |
'언어 자료구조 알고리즘 > Escort C#' 카테고리의 다른 글
[C#] 5.1.4 인덱서 (2) | 2016.05.02 |
---|---|
[C#] 5.1.3 메서드와 매개 변수 전달 방식 (0) | 2016.05.02 |
[C#] 5.1.2 멤버 속성 (0) | 2016.05.02 |
[C#] 5.1 캡슐화 대상(멤버) - 멤버 필드 (0) | 2016.05.02 |
[C#] 5. 캡슐화 (0) | 2016.05.02 |
[C#] 3.4 string (0) | 2016.04.01 |
[C#] 3.3 배열 (0) | 2016.04.01 |
[C#] 3.2 Boxing 과 UnBoxing (0) | 2016.04.01 |
[C#] 3. 형식 개요, 3.1 object (0) | 2016.04.01 |
[C#] 2.2.2 식과 문 (0) | 2016.04.01 |