전체 페이지뷰

2016년 12월 10일 토요일

C# 데이터형



프로그래밍이란 결국 데이터를 얻고 가공하여 새로운 정보를 얻는 일인것 같습니다.
그것을 잘 다루기 위해서 많은 데이터의 형식이 생겨납니다.
그러나 프로그래밍 언어들 중에서는 Python처럼 그 형식을 엄격하게 지정하지 않고 컴퓨터가 유추하게 하는 프로그램이 있는가 하면 C계열의 언어들처럼 강력하게 타입을 제한해 두는 경우가 있습니다.


각각에는 제가 생각하기에 장단점이 존재합니다.
Weak typing의 경우, 일단은 프로그래머가 사용하기가 편리합니다. 골치 아프게 형을 지정할 필요도 없고 그냥 변수를 만들어 그때그때 필요한 값을 넘겨주면 그만입니다. 그렇게 사람이 편한 대신 컴퓨터가 더 많은 일을 해야 하므로 언제나 성능 문제를 끌고 다니며(최근엔 별 차이 없다곤 하지만) 디버깅이 어렵게 되는 단점과 IDE 상에서 인텔리센스같은 보조를 받기 어렵다는 단점이 있습니다. 그리하여 작성 시점에 이미 알아채야할 오타같은 것을 찾아 내기가 어렵습니다. 하지만 그 유연함은 정말 매력적입니다.

반면 strong typing의 경우 프로그래머에게 한번 더 생각하게 만듭니다. 어떤 변수를 어떤 형식으로 사용할 것인가를 제한해야 하니까요. 그러나 잘못된 형식의 데이터는 애초에 다룰 수가 없으므로 버그의 여지를 줄여줍니다. 아주 큰 프로젝트에선 더욱더 중요한 일이겠지요.

C#은 strong typing의 언어이므로 우리는 데이터형에 대해 잘 알아두어야 합니다.

기본데이터 vs 복합데이터

기본데이터: 정수, 부동소수점, 문자 등
복합데이터: 구조체, 클래스, 배열 등 기본데이터를 토대로 만들어진 데이터형

값형식(value type) vs 참조형식(reference type)

이 둘을 이해하기 위해 먼저 스택(Stack)과 힙(Heap)의 두가지 메모리 구조에 대해 알아보겠습니다.

스택과 값형식

스택은 다들 아시겠지만 LIFO(Last in first out)의 행태를 보이는 선형구조입니다.
값형식은 이 스택에 저장되며, 코드상에 나온 순서대로 쌓였다가 코드블럭이 끝나는 }를 만나면 나중에 들어온 값부터 차례로 메모리에서 제거됩니다.

힙과 참조형식

스택에 저장된 값형식이 이렇게 알아서 제거되는 반면에 힙은 스스로 제거되지 않으며 대신에 CLR의 가비지컬렉터(garbage collector)가 더 이상 참조되지 않는 객체를 수거합니다.

값형식의 데이터는 코드블럭이 끝나면 사라져 버리므로 그와는 무관하게 데이터를 유지하고 싶을 때 힙에 할당이 되는 것입니다.

이 참조 형식의 변수는 힙과 스택을 동시에 이용하는데 힙 영역에 데이터를 저장하고 스택에는 그 데이터가 저장되어 있는 메모리 주소를 저장합니다. 그 메모리 주소를 보고 데이터를 찾기 때문에 '참조'라는 말이 붙는 것입니다.

이제 기본 데이터 형식에 대해 알아 보겠습니다.

기본 데이터 형식(Primitive types)

모두 15가지가 있으며 문자열, 오브젝트 형만 제외하곤 모두 값형식입니다.
이중 가장 많은 숫자 데이터 형식(정수, 부동 소수, 소수)부터 알아봅니다.

1. 정수 

형식범위크기
sbyte-128 ~ 127부호 있는 8비트 정수
byte0 ~ 255부호 없는 8비트 정수
charU+0000 ~ U+ffff유니코드 16비트 문자
short-32,768 ~ 32,767부호 있는 16비트 정수
ushort0 ~ 65,535부호 없는 16비트 정수
int-2,147,483,648 ~ 2,147,483,647부호 있는 32비트 정수
uint0 ~ 4,294,967,295부호 없는 32비트 정수
long-9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807부호 있는 64비트 정수
ulong0 ~ 18,446,744,073,709,551,615부호 없는 64비트 정수
모두 long으로 하면 여기에 해당하는 수를 전부 나타낼수 있지만 메모리 낭비가 심해지므로 각각 담을 수 있는 숫자 범위에 따라 세분해 둔 것이라 할 수 있습니다.

만약 변수가 담을 수 없는 범위의 값을 지정한다면 어떻게 될까요?
위로는 overflow, 아래로는 underflow가 일어나 각각 변수가 담을 수 있는  최소값, 최대값으로 변해버립니다. 조심합시다.
*최대값, 최소값 속성 MaxValue, MinValue
int a = int.MaxValue;
double b = double.MinValue;
cs
와 같이 사용합니다.

2.부동소수점

형식근사 범위전체 자릿수
float±1.5e−45 ~ ±3.4e387개의 자릿수
double±5.0e−324 ~ ±1.7e30815-16개의 자릿수
 예)
float a = 3.141592f
double b = 3.14159265358978
float 뒤에는 f를 붙여서 float형임을 명시해줍니다.

3. decimal 형식

형식근사 범위전체 자릿수.NET Framework 형식
decimal(-7.9 x 1028 - 7.9 x 1028) / (100 - 28)28-29개의 유효 자릿수System.Decimal
decimal(십진수)도 실수를 다룹니다만 부동소수점과는 다른 방식이며 정밀도가 훨씬 높습니다(끝에 m을 붙여 decimal임을 명시합니다).

여기서 float, double, decimal의 차이를 나타내는 예제를 한번 들어보겠습니다.
using System;
namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            float a = 0.111111111122222222223333333333f;
            double b = 0.111111111122222222223333333333;
            decimal c = 0.111111111122222222223333333333m;
            Console.WriteLine(a);
            Console.WriteLine(b);
            Console.WriteLine(c);
        }
    }
}
cs

0.1111111
0.111111111122222
0.1111111111222222222233333333
계속하려면 아무 키나 누르십시오 . . .

CTRL+F5를 눌러 실행해보면 아래와 같이 그 정밀도의 차이를 여실히 보여줍니다.

4. 문자와 문자열 형식

형식범위크기.NET Framework 형식
charU + 0000 ~ U + FFFF유니코드 16비트 문자System.Char
문자(char)는 위와 같이 16비트 유니코드로 문자 하나를 나타낼 때 쓰입니다.
그리고 문자열(string)은 그런 문자 여러개로 이루어져 있습니다. 
char는 작은 따옴표(' ')로 묶어주고, 문자열은 큰따옴표(" ")로 묶어줍니다.

char c = '안';            // 문자상수는 작은 따옴표
string s = "안녕하세요";   // 문자열은 큰 따옴표
cs

5. 논리 형식(Boolean Type)

true나 false만을 지정할 수 있는 1바이트 형식입니다. C 언어에서 처럼 정수값이 아닌 다른 형식이며 따라서 정수형식으로 변환할 수 없습니다. 1비트면 나타내는데 충분하지만 기본값이 바이트이므로 1바이트의 크기를 지닙니다.

bool a = true;
bool b = false;
cs
와 같이 사용합니다.


6. object 형식

오브젝트는 C#에 있는 모든 데이터형식(기본 데이터, 복합데이터 전부)의 부모가 됩니다.
다시 말해 모든 데이터 형식은 object를 상속받아 만들어집니다. 그래서 어떤 데이터라도 object에 담을 수 있습니다. (따라서 모든 데이터형은 objcet가 가지는 자체 메소드 GetType(), ToString()...을 사용할 수 있습니다.)

모든 기본 데이터 중, string과 object는 값형식이 아닌 참조형식입니다.
string은 char로 이루어진 포인터라 할 수 있으니 참조형식이 당연할것 같고,
object는 어째서 참조형식인걸까요?

그를 위해서는 박싱(boxing)과 언박싱(unboxing)에 대해 알아야 합니다.

박싱은 말 그대로 상자에 담는다는 말이고, 언박싱은 상자를 푼다는 말입니다.

object a = 1;
cs

이런 코드가 있다고 가정해봅시다.
현재 오브젝트는 참조형이므로 현재 1이라는 숫자는 힙에 저장되어 있으며,
a라는 변수는 스택에 존재하면서 1이라는 숫자가 저장된 주소를 가리키고 있습니다.
1이라는 수를 힙에 쳐박아두고, 그 주소만 받아 쥐고 있는 이 상태를 박싱이라 합니다.

그런데, 그 뒤에 
int b = (int)a;
cs
이런 일이 생겼다고 해봅시다. b는 값형식인 정수 변수 입니다.
그러면 b는 a가 저장되어 있는 힙으로부터 그 값을 복사하여 받아와 스택에 저장합니다.
이것을 언박싱이라고 합니다.


지금까지 기본 데이터 형식에 대해 알아봤습니다.

댓글 없음:

댓글 쓰기