뇌를 자극하는 C# 4.0 프로그래밍/ 데이터 보관하기

데이터에도 종류가 있다

  • 데이터 형식은 ‘기본 데이터 형식 : 복합 데이터 형식’이나 ‘값 형식 : 참조 형식’으로 구분할 수 있다.

변수(Variable)

  • 아래와 같이 변수를 선언하면 컴파일러는 선언된 int 형식을 위해 메모리 공간을 할당하고 이 공간을 x라는 식별자가 사용할 수 있도록 준비해 둔다.

  • 선언된 변수에는 대입 연산자를 통해 데이터를 입력할 수 있는데 이 코드가 실행되면 x를 위해 할당된 메모리 공간에 데이터 100이 기록된다.

값 형식(Value Types)과 참조 형식(Reference Types)

  • 값 형식은 변수가 값을 담고, 참조 형식은 변수가 값이 있는 곳의 위치(참조)를 담는다.
  • 메모리 영역은 스택(Stack)과 힙(Heap)으로 구분할 수 있는데, 스택은 값 형식과 관련 있고, 힙은 참조 형식과 관련이 있다.

스택과 값 형식

  • 스택은 먼저 들어간 데이터가 아래에 쌓이고 나중에 들어간 데이터가 위에 쌓이는 형식. 먼저 들어간 데이터를 꺼내려면 그 위에 쌓인 데이터들을 모두 걷어내야 한다.

  • 값 형식의 변수들은 모두 스택에 저장된다.
  • 코드 블록 안에 생성된 모든 값형 변수들은 코드 블록이 끝나면 —} 를 만나면– 메모리에서 제거된다.

힙과 참조 형식

  • 스택은 자신이 담고 있던 데이터를 모두 제거하지만, 힙은 저장되어 있는 데이터를 스스로 제거하는 메커니즘이 없다. 힙의 데이터를 제거하는 역할은 CLR의 가비지 컬렉터가 담당 한다.
    • 가비지 컬렉터는 프로그램 뒤에 숨어 동작하면서 힙에 더는 사용하지 않는 객체가 있으면 그 객체를 쓰레기로 간주하고 수거하는 기능을 한다.
    • 힙이라는 메모리 영역이 필요한 이유는 코드 블록이 끝난 후에도 데이터를 유지하고 싶기 때문. 스택은 코드 블록이 끝나면 데이터가 사라지기 때문에 한계가 있다.
  • 참조 형식의 변수는 힙과 스택을 모두 사용한다. 힙 영역에는 데이터를 저장하고, 스택 영역에는 데이터가 저장되어 있는 힙 메모리의 주소를 저장한다.
    • 힙의 데이터를 참조 하고 있는 스택이 없으면 가비지 컬렉터가 수거해 간다.
    • 여기서 중요한 점은 가비지 컬렉터가 힙의 쓰레기를 언제 수거해 갈지 알 수 없다는 것. 이 부분은 최적화 이슈와도 연결된다.

기본 데이터 형식(Primitive Types)

  • C#이 제공하는 기본 데이터 형식은 모두 15가지가 존재하는데, 이들은 크게 숫자 형식, 논리 형식, 문자열 형식, 오브젝트 형식으로 구분된다.
    • 이 중 문자열 형식과 오브젝트 형식만 참조 형식이며 나머지는 모두 값 형식이다.

숫자 데이터 형식(Numeric Types)

  • 프로그래밍을 구성하는 가장 많은 데이터 형식이 숫자. 이유는 다른 복잡한 데이터가 숫자를 기반으로 구성되기 때문.
    • 텍스트 데이터도 알고 보면 각 문자 하나 하나가 내부적으로 숫자 코드로 구성되어 있다. ex) a는 63, b는 64

정수 계열 형식(Integral Types)

데이터 형식 설명 크기(Byte) 담을 수 있는 값의 범위
byte 부호 없는 정수 1 (8 bit) 0 ~ 255
sbyte signed byte 정수 1 (8 bit) -128 ~ 127
short 정수 2 (16 bit) -32,768 ~ 32,767
ushort unsigned short 정수 2 (16 bit) 0 ~ 65535
int 정수 4 (32 bit) -2,147,483,648 ~ 2,147,483,647
uint unsigned int 정수 4 (32 bit) 0 ~ 4,294,967,295
long 정수 8 (64 bit) -922,337,203,685,477,508 ~ -922,337,203,685,477,507
ulong unsigned long 정수 8 (64 bit) 0 ~ 18,446,744,073,709,551,615
char 유니코드 문자 2 (16 bit)

부호 있는 정수와 부호 없는 정수

  • 부호가 있는 데이터 형식은 가장 앞에 있는 비트를 부호로 사용한다.
    • 아래의 이미지는 -127일 것 같지만 사실은 -1 값이 된다. 이유는 그 아래의 설명 참조.

  • 직관적으로 생각하기엔 음수는 맨 앞의 비트를 1로 표현하고 나머지는 본래 숫자 자체로 표현할 것 같은데 –이런 방법을 부호와 절대값 방식 (Sign-and-magnitude)이라고 한다– 이 방식을 사용하면 0이 +0(0000 0000)과 -0(1000 0000)으로 표현되는 문제가 발생한다.
  • 이러한 문제를 해결하기 위해 음수 표현은 2의 보수법 (2’s Complement)이라는 방식을 사용하는데 그 표현 방법은 다음과 같다.
    1. 먼저 수 부분 비트를 채운다.
    2. 전체 비트를 반전 시킨다.
    3. 반전된 비트에 1을 더한다.
  • 아래의 이미지는 -1을 2의 보수법으로 표현한 예

데이터가 넘쳐 흘러요

  • 데이터 형식의 크기를 넘어서는 값을 담게 될 때 나타나는 현상을 오버플로우 (Overflow)라고 한다.
    • byte의 최대값은 1111 1111(255)인데 여기에 1을 더하면 1 0000 0000이 된다. byte는 8개의 비트만 담을 수 있기 때문에 왼쪽의 비트는 버리고 오른쪽의 8개의 비트만 보관하기 때문에 byte 형식의 변수가 오버플로우가 되면 0이 된다.
  • 이와 반대로 값 형식의 최저값보다 작은 데이터를 저장하면 언더플로우 (Underflow)가 일어난다.

부동 소수점 형식(Floating Point Types)

  • 부동 소수점이란 소수점이 고정되어 있지 않고 움직이면서 수를 표현한다는 뜻이다.
    • 이 때문에 부동 소수점 방식은 정밀도에 한계가 있음.
데이터 형식 설명 크기(Byte) 담을 수 있는 값의 범위
float 단일 정밀도 부동 소수점 형식 (7개의 자릿수만 다룰 수 있음) 4 (32 bit) -3.402823e38 ~ 3.402823e38
double 복수 정밀도 부동 소수점 형식 (15-16개의 자릿수를 다룰 수 있음) 8 (64 bit) -1.79769313486232e308 ~ 1.79769313486232e308
  • 부동 소수점 형식은 실수 영역의 데이터를 다룰 수 있지만 다음과 같은 이유로 정수 형식을 대체할 수 없다.
    1. 소수점을 표현하기 위해 일부 비트를 사용하기 때문에 같은 크기의 정수 계열 형식과 같은 크기의 수를 표현할 수 없다.
    2. 산술 연산 과정이 정수 계열 형식보다 복잡해서 느리다.

  • float 형식의 경우 맨 앞의 1비트를 부호 전용으로 사용하고, 그 다음 8비트를 소수점의 위치를 나타내는 지수부로 사용하고, 나머지 23비트를 수를 표현하는 가수부로 사용한다.
  • float는 -3.402823e38(-3.402823 x 1038) ~ 3.402823e38(3.402823 x 1038)에 이르는 어마어마한 크기를 다룰 수 있지만 유효숫자는 7자리 밖에 되지 않는다. 7자리를 넘어가는 수는 ‘대략적으로’ 표현한다는 뜻.

Decimal 형식

데이터 형식 설명 크기(Byte) 담을 수 있는 값의 범위
decimal 29자리 데이터를 표현할 수 있는 소수 형식 16 (128 bit) ±1.0 x 10e-28 ~ ±7.9 x 10e28
  • decimal도 범위의 한계는 있지만, float이나 double보다 높은 정밀도를 나타내기 때문에 큰 수를 다뤄야 하는 경우에 유용하다.

문자 형식과 문자열 형식

  • char 형식은 정수를 다루는 데이터 형식이지만 문자 데이터를 다룬다.
    • 작은 따옴표 '를 사용한다.
  • string은 여러 char가 묶여 있는 데이터 형식. string 형식은 참조형이기 때문에 정해진 크기나 담을 수 있는 데이터의 범위가 따로 정해져 있지 않다.
    • 큰 따옴표 "를 사용한다.

논리 형식

데이터 형식 설명 크기(Byte) 담을 수 있는 값의 범위
bool 논리 형식 1 (8 bit) true, false
  • C 언어에서는 논리 형식이 없었기 때문에 0을 거짓 0이 아니면 참으로 사용했었다.

object 형식

  • C#에서는 object가 모든 데이터를 다룰 수 있도록 모든 데이터 형식이 object 형식을 상속 받도록 정의해 놨다.

박싱(boxing)과 언박싱(unboxing)

  • object 형식은 값 형식의 데이터를 힙에 할당하기 위한 ‘박싱(boxing)’ 기능을 제공한다. object 형식에 값 형식의 데이터를 할당하려는 시도가 이루어지면 object 형식은 박싱을 수행해서 해당 데이터를 힙에 할당한다.

  • 이와 반대로 힙에 있던 값 형식 데이터를 값 형식 객체에 다시 할당해야 하는 경우가 있는데 이를 ‘언박싱(unboxing)’이라고 한다.

데이터 형식 바꾸기

크기가 서로 다른 정수 형식 사이의 변환

  • 작은 정수 형식의 변수에 있는 데이터를 큰 정수 형식의 변수로 옮길 때는 문제가 없지만 그 반대의 경우 오버플로우가 발생할 수 있다. 물론 큰 변수로부터 옮겨오는 데이터가 작은 형식의 변수가 담을 수 있는 크기라면 문제 없다.

크기가 서로 다른 부동 소수점 형식 사이의 변환

  • 부동 소수점 형식의 특성상 float과 double의 사이의 변환에 오버플로우는 존재하지 않는다. 다만 정밀도에 손상이 생길 수는 있다.

부동 소수점 형식과 정수 형식 사이의 변환

  • 부동 소수점 형식의 변수를 정수 형식으로 변환하면 데이터에서 소수점 아래는 버리고 소수점 위의 값만 남긴다. 0.1이나 0.9나 모두 정수 형식으로 변환하면 0이 된다.

문자열을 숫자로, 숫자를 문자열로

int a = int.Parse("12345");
float b = float.Parse("123.45");
string c = a.ToString();
  • 문자열을 숫자로, 숫자열을 문자로 변환하려고 하면 컴파일도 되지 않는다. 그러나 이 형식 변환은 매우 자주 있는 일이기 때문에 C#에서는 이 둘 사이의 변환에 위와 같은 별도의 방법을 마련해 주고 있다.
    • 문자열을 int나 float 형식으로 바꿀 때는 Parse() 메소드를 사용하고, 숫자 형식을 문자열로 바꿀 때는 ToString() 메소드를 사용한다.

상수(Constants)와 열거 형식(Enumerator)

상수 – 전 언제나 변하지 않을 거에요

const int a = 3;
const double b = 3.14;
const string c = "abcde";
  • 상수는 const 키워드를 데이터 타입 앞에 붙이면 된다.
    • 상수는 실행 중에 바꿀 수 없기 때문에 선언 시에 값을 넣어줘야 한다.

열거 형식 – 여러 개의 상수를 정리 합시다

// 열거형 선언
enum Dialog { Yes, No, Cancel, Confirm, OK }

// 열거형 변수 사용
Dialog result = Dialog.Yes;
  • 실제 프로그래밍시에 대단히 유용한 자료형. 상수이고 범위가 제한적이기 때문에 대단히 안전하고, 사용할 때 위와 같이 문자 형태로 표현되기 때문에 직관성도 높다.
  • 또한 각 열거형 상수가 값을 갖고 있기 때문에 숫자형 데이터로 변환하거나 숫자형 데이터를 열거형으로 바꾸는데도 문제가 없다.
    • 따로 값을 대입할 수도 있지만 아무런 값도 넣지 않으면 컴파일러가 0부터 1씩 증가하여 알아서 값을 넣어준다.

Nullable 형식

int? a = null;
float? b = null;
doublie? c = null;
  • 기본적으로 값 형식의 변수는 null 값을 가질 수 없는데, 만일 값 형식의 변수가 null 값을 갖도록 하려면 위와 같이 형식 이름 뒤에 ?를 붙이면 된다.
    • 이렇게 선언하면 컴파일러는 해당 변수에게 할당된 메모리 공간을 비워둔다.

var: 데이터 형식을 알아서 파악하는 똑똑한 C# 컴파일러

var a = 3; // a는 int 형식
var b = "Hello"; // b는 string 형식
  • 기본적으로 C#은 형식 검사를 강력하게 하는 언어이기 때문에 선언할 때 형식을 지정해 줘야 하지만 var 라는 키워드를 통해 어떠한 타입의 형식도 받을 수 있는 변수를 선언할 수 있다.
  • 다만 var 타입의 변수는 선언할 때 반드시 값을 받아야 한다. 이는 컴파일 단계에서 컴파일러가 어떤 타입의 변수인지 알아야 하기 때문.
  • 개인적인 성향상 var 키워드의 가독성이 떨어지고 엄격함이 떨어져서 선호하지 않는데, enumerator 형식의 복잡하게 중첩된 자료형의 경우 —List<Dictionary<int, string>>와 같은– var를 쓰면 타이핑을 줄일 수 있어서 편하긴 하다.

공용 형식 시스템(Common Type System)

System.Int32 a = 123;
System.String c = "abc";
  • 공용 형식 시스템이란 .NET 프레임워크를 지원하는 모든 언어들이 따르는 데이터 형식 표준. .NET 언어들끼리의 호환성을 높이기 위해 도입되었다.
  • 만일 위와 같이 변수를 선언하면 .NET 언어 어디서든 그대로 사용될 수 있다.
Class Name C# 형식 C++ 형식 비주얼 베이직 형식
System.Byte byte unsigned char Byte
System.SByte sbyte char SByte
System.Int16 short short Short
System.Int32 int int 또는 long Integer
System.Int64 long _int64 Long
System.UInt16 ushort unsigned short UShort
System.UInt32 uint unsigned int 또는 unsigned long UInteger
System.UInt64 ulong unsigned _int64 ULong
System.Single float float Single
System.Double double double Double
System.Boolean bool bool Boolean
System.Char char wchar_t Char
System.Decimal decimal Decimal Decimal
System.IntPtr 없음 없음 없음
System.UIntPtr 없음 없음 없음
System.Object object Object* Object
System.String string String* String
[ssba]

The author

지성을 추구하는 디자이너/ suyeongpark@abyne.com

댓글 남기기