기본자료형
##### 변수(Variable)란 프로그램에서 일시적으로 데이터를 저장하는 공간 즉 메모리의 일부를 의미하고, 저장된 데이터는 변경이 가능하다. ##### 상수(Constant)란 프로그램에서 항구적으로 데이터를 저장하는 공간으로 저장된 데이터의 변경이 불가능한 공간이다.
##### 변수가 왜 필요한가? 데이터가 입력되면 어딘가에 저장해야만 다음에 사용할 수가 있기 때문이다. 변수= 타입+ 이름+ 데이터 타입은 변수를 어떻게 다룰지를 컴파일러에게 알려주기 위한 정보로 자료형이라고도 불린다.
[정수형 타입]
타입 | 값 | 설명 | .NET 타입 | 기본값 |
---|---|---|---|---|
sbyte | -128 ~ 127 | 부호있는 8비트 정수 | System.Sbyte | 0 |
byte | 0 ~ 255 | 부호없는 8비트 정수 | System.Byte | 0 |
short | -32,768 ~ 32,767 | 부호있는 16비트 정수 | System.Int16 | 0 |
ushort | 0 ~ 65,535 | 부호없는 16비트 정수 | System.UInt16 | 0 |
int | -2,147,483,648 ~ 2,147,483,647 | 부호있는 32비트 정수 | System.Int32 | 0 |
uint | 0 ~ 2^32 | 부호없는 32비트 정수 | System.UInt32 | 0 |
long | -2^63 ~ 2^63 | 부호있는 64비트 정수 | System.Int64 | 0L |
ulong | 0 ~ 2^64 | 부호없는 64비트 정수 | System.UInt64 | 0 |
[실수형 타입]
타입 | 값 | 설명 | .NET 타입 | 기본값 |
---|---|---|---|---|
float | ±1.5×10^−45 ~ ±3.4 × 10^38 | 7자릿수를 가지는 4바이트 실수 | System.Single | 0.0F |
double | ±5×10^−324 ~ ±1.7 × 10^308 | 12~16 자릿수를 가지는 8바이트 실수 | System.Double | 0.0D |
decimal | ±1.0 × 10^−28 ~ ±7.9 × 10^28 | 28~29개의 유효자릿수를 가지는 16바이트 실수 | System.Decimal | 0.0M |
[문자형 타입]
타입 | 값 | 설명 | .NET 타입 | 기본값 |
---|---|---|---|---|
char | U+0000 ~ U+ffff (UTF-16LE) | 유니코드16비트문자 | System.Char | '\0' |
string | 문자열(UTF-16LE) | 유니코드문자열 | System.String | null |
C# 은 String 인스턴스의 모든 바이트 배열을 무조건 UTF-16 LE방식으로 인코딩시켜 메모리에 저장한다.
“XY” 문자열에 대한 UTF-16LE 저장방식은 아래와 같다.
[Boolean형 타입]
타입 | 값 | 설명 | .NET 타입 | 기본값 |
---|---|---|---|---|
bool | true, false | 8비트값 | System.Boolean | false |
stack vs. heap
프로그램을 실행 시키면 운영체제는 우리가 실행시킨 프로그램을 위해 메모리 공간을 할당 해준다. 할당되는 메모리 공간은 크게 스택(stack), 힙(heap), 데이터(data)영역으로 나뉘어진다

데이터 영역:
1. 전역변수와 static변수가 할당되는 공간이다.
2. 프로그램 시작과 동시에 할당되고, 프로그램 종료 시 메모리에서 소멸된다
int a = 10; // 데이터영역에할당
int b = 20; // 데이터영역에할당
static void Main(string[] args)
{
...
}

스택영역:
1. 함수 호출 시 생성되는 지역변수와 매개변수가 저장되는 영역이다.
2. 함수 호출이 완료되면 사라진다
3. 운영체제가 할당해준 스택영역을 넘어 사용하게 되면 stack overflow가 발생한다.
int a = 10; // 데이터 영역에 할당
int b = 20; // 데이터 영역에 할당
static void Main(string[] args)
{
int i = 100; // 지역변수 i가 스택 영역에 할당
fct1(i);
fct2(i);
}
static void fct1(int c)
{
int d = 30; // 매개변수 c와 지역변수 d가 스택영역에 할당
}
static void fct2(int e)
{
int f = 40; // 매개변수 e와 지역변수 f가 스택영역에 할당
}

힙영역:
1. 필요에 의해 동적으로 메모리를 할당 할 때 사용 한다.
2. CRL에서 정확하게는 GC(garbage collector)에서 할당 및 해제를 관리한다.
3. New로 할당되는 모든 참조형 객체는 힙에 할당된다.

value type vs. reference type
C#에는 값 형식(value type)과 참조 형식(reference type)이라는 두 가지 데이터 형식이 존재한다.
값형식:
1. 스택 영역에 메모리가 할당되는 형식

참조형식:
1. 스택에는 참조 주소를 저장할 공간이, 힙에는 실제 값을 저장하는 메모리가 할당되는 형식
2. 초기화 되지 않은 모든 참조형 변수는 null값을 기본값으로 가진다

Boxing:
값 형식을 참조 형식으로 변환하는 것
Unboxing:
참조 형식을 값 형식으로 변환하는 것
static void Main()
{
int a = 5;
// boxing
//obj는 지역변수로 스택에 할당되고, 동시에 참조형 변수이기 때문에 힙에도 메모리가 할당되며, 변수 a의 값(5)이 복사 된다.
object obj = a;
// unboxing
//변수 b는 지역변수로 스택에 할당되고, obj의 참조 값(5)을 복사해 온다.
int b = (int) obj;
}
형 변환이 일어나는 unboxing의 경우도 성능에 좋지는 않지만, 힙 영역의 메모리 할당/해제를 발생시키고 이는 결국 GC(garbage collection)이 바쁘게 돌아가도록 하여 프로그램 성능에 부하를 주는 boxing의 경우 사용을 자제해야 한다.
var 타입
C#3.0부터 추가된 var키워드를 통해 지역 변수의 타입을 명시하지 않아도 컴파일러가 타입을 유추할 수 있도록 할 수 있다.
1. var키워드는 런타임에 바인딩 되지 않고 컴파일 타임에 컴파일러가 적합한 타입을 결정하고 할당하게 된다. 때문에 전역 변수 선언에는 사용할 수 없다.
2. 컴파일러가 적합한 타입을 결정 하려면 선언과 동시에 초기화가 이루어져야 한다. 초기화가 되어있지 않으면 컴파일 에러가 발생한다.
3. null값으로 초기화 할 수 없다.
4. 클래스 내에서 사용 할 수 없다.
5. 사용자 정의 타입(struct or class)에 사용 가능하다.
Class Person
{
public string FirstName;
public string LastName;
public int Age;
}
Person person = new Person();
person.FirstName = "경균";
person.LastName = "김";
person.Age = 19;
//사용자 지정 타입으로 유추
var p = person;
6. 익명타입(anonymouse)에 사용 가능하다.
var p = new { FirstName = "cris" LastName = "jeong" Age = 19 };
#### 컴파일러는 타입 유추(type inference)를 어떻게 하는가? > var키워드는 ‘=’ 키워드를 기준으로 오른쪽 부분의 형태에 따라 컴파일러에게 타입유추를 지시한다.
실제 타입유추가 일어나는지 확인을 위해 예제 코드를 만들어서 컴파일 하고, 만들어진 assembly를 IL DisAssembler Tool (ildsm.exe)을 사용하여
컴파일된 IL코드를 확인해 보면 아래와 같이 var키워드로 선언된 변수들이 타입유추가 된 것을 확인 할 수 있다.
namespace ConsoleApplication1
{
class Program
{
class Person
{
public string name;
public int age;
}
static void Main(string[] args)
{
var varInt = 2;
var varString = "varString";
var person = new Person();
person.age = 10;
person.name = "cris";
Console.WriteLine(varInt);
Console.WriteLine(varString);
Console.WriteLine("name={0}, age={1}", person.name, person.age);
Console.ReadLine();
}
}
}

Nullable 타입
nullable 형식은 다음과 같은 특징이 있다.
1. Nullable<T>타입은 일반적인 값 형식(value type)에 대해 null표현이 가능하게 하는 역할을 한다.
2. Reference type형식을 기반으로 nullable형식을 만들 수는 없다.
A. T는 value type만 가능하다는 이야기다.
B. Nullalbe<string>은 허용되지 안는다.
3. T? 형태로 Nullable<T>구문을 축약해서 사용할 수 있다.
A. 값 할당은 일반 value type과 같은 방법으로 할당한다.
B. int? intNullable = null; 도 가능하다.
4. HasValue, Value 2가지 읽기 전용 속성을 사용하여 null에 대해 테스트 하고 값을 읽어올 수 있다.
A. HasValue 속성은 값이 있으면 true를 null이면 false를 반환한다.
B. HasValue의 기본값은 false이고 Value의 기본값은 없다.
C. 중첩된 nullable 형식은 허용되지 않습니다. Nullable<Nullable<int>> n;과 같은 경우 컴파일 에러가 된다.
구조체
구조체의 특징은 다음과 같다.
1. 구조체는 value type으로 스택(stack)에 저장된다.
2. 구조체는 다른 구조체 또는 클래스에서 상속될 수 없다.
3. 구조체는 생성자를 선언할 수 있으나 반드시 매개 변수를 사용해야 한다.
4. 구조체 선언시(=new를 사용하지 않은 인스턴스화) 구조체의 멤버를 개별적으로 초기화 해야 한다.
5. new를 통해 인스턴스화 할 수 있고, 이경우 생성된 후에 생성자가 호출되거나 생성자가 없다면 멤버는 0으로 초기화 된다.
6. 구조체의 어떤 멤버를 가상으로 선언하는 것은 불가능하다.
제어문
제어문의 종류와 그 사용법을 정리한다.
범주 | 종류 |
---|---|
선택문 | if, switch |
반복문 | for, foreach, while, do/while |
점프문 | break, continue, goto, return, throw 주의) break는반복문내에서만사용가능함. |
C / C++과의 구별되는 내용은 아래 따로 정리 한다.
foreach문:
1. 배열이나 컬렉션에서 해당 원소들을 얻어올 때 사용한다.
2. foreach (표현식_자료형 변수명 in 표현식) 으로 작성한다.
int [] arr = new int[] { 1, 2, 3, 4, 5 };
foreach (int elem in arr) {
Console.WriteLine(elem);
}
switch문의 표현:
//문자열을 표현식으로 사용할 수 있다.
static void Main()
{
string value = "turnip";
// ... Switch on the string.
switch (value)
{
case "lettuce":
Console.WriteLine("LETTUCE");
break;
case "squash":
Console.WriteLine("SQUASH");
break;
case "turnip":
Console.WriteLine("TURNIP");
break;
}
}
//enum타입을 표현식으로 사용할 수 있다.
enum Priority
{
Zero,
Low,
Medium,
Important
};
class Program
{
static void Main()
{
Priority priority = Priority.Zero;
Boolean bRetValue;
switch (priority)
{
case Priority.Low:
case Priority.Medium:
case Priority.Zero:
default:
bRetValue = false;
break;
case Priority.Important:
bRetValue = true;
}
}
연산자
연산자의 종류와 그 내용을 정리한다.
[관계 연산자]
연산자 | 설명 | 예제 |
---|---|---|
> | 왼쪽 값이 오른쪽 값보다 크면 참, 같거나 작으면 거짓 | bool result = 10 > 20; // 거짓 |
< | 왼쪽 값이 오른쪽 값보다 같거나 작으면 참, 크면 거짓 | bool result = 10 < 20; //참 |
>= | 왼쪽 값이 오른쪽 값보다 크거나 같으면 참, 작으면 거짓 | bool result = 10 >= 20; //거짓 |
<= | 왼쪽 값이 오른쪽 값보다 작으면 참, 크거나 같으면 거짓 | bool result = 10 <= 20; //참 |
== | 왼쪽 값과 오른쪽 값이 같으면 참, 다르면 거짓 | bool result = 10 == 20; //거짓 |
!= | 왼쪽 값과 오른쪽 값이 다르면 참, 같으면 거짓 | bool result = 10 != 20; //참 |
[논리 연산자]
연산자 | 설명 | 예제 |
---|---|---|
&& | AND연산자로 양쪽이 true or false인 경우 true | bool result = true && false; //false |
^ | XOR연산자로 좌우의 값이 같으면 false, 다르면 true | bool result = true ^ false;//true |
! | 부정연산자로 논리 부정을 계산한다. tree이면 false를, false면 true가 된다. | bool result = !true; //flase |
& | 두 피연산자의 대응되는 비트에 대한 논리곱을 수행 | int a = 1, b = 2, c;c = a & b; //0 |
두 피연산자의 대응되는 비트에 대한 논리합을 수행 |
[산술 연산자]
연산자 | 설명 | 예제 |
---|---|---|
+ | 양쪽 피연산자를 더한다, | a + b |
- | 왼쪽 피연산자에서 오른쪽 피연산자를 뺀다. | a - b |
* | 양쪽 피연산자를 서로 곱한다. | a * b |
/ | 왼쪽 피연산자를 오른쪽 피연산자로 나눈다. | a / b |
% | 왼쪽 피연산자를 오른쪽 피연산자로 나눈뒤의 나머지를 구한다. | a % b |
[증감 연산자]
연산자 | 설명 | 예제 |
---|---|---|
++ | 피연산자의 값을 1 증가시킨다. | int n=50;n++; //51 |
-- | 피연산자의 값을 1 감소시킨다. | int n=50;n--; //49 |
증감 연산자는 연산자위 위치에 따라 전위/후위 표기법이 있다. 연산자의 위치에 따라 동작 방식이 바뀐다.
int n = 50;
Console.WriteLine(n++); //화면에 50을 출력하고, n값을 1증가 시킨다.
n = 50
Console.WriteLine(++n); //n값을 1증가 시키고, 화면에 51을 출력한다.
[복합대입 연산자]
연산자 | 설명 | 예제 |
---|---|---|
+= | 오른쪽 피연산자를 왼쪽 피연산자에 더하고 그 결과를 왼쪽 연산자에 대입한다. | int n = 50;n += 5; //55 |
-= | 오른쪽 피연산자를 왼쪽 피연산자에서 빼고 그 결과를 왼쪽 연산자에 대입한다. | int n = 50;n -= 5; //45 |
*= | 오른쪽 피연산자를 왼쪽 피연산자에 곱하고 그 결과를 왼쪽 연산자에 대입한다. | int n = 50;n *= 5; //250 |
/= | 오른쪽 피연산자로 왼쪽 피연산자를 나누고 그 결과를 왼쪽 연산자에 대입한다. | int n = 50;n /= 5; //10 |
%= | 오른쪽 피연산자로 왼쪽 피연산자의 나머지 값을 구하고 그 결과를 왼쪽 연산자에 대입한다. | int n = 50;n %= 5; //0 |