본문 바로가기
IT/C#(CS)

CS 기초강좌 6. 값 타입과 참조 타입, 스택과 힙, struct와 class

by 신림83 2020. 11. 11.
반응형

CS 기초강좌 6. 값 타입과 참조 타입, 스택과 힙, struct와 class

 사용자가 코드를 통하여 객체를 생성할 때, 크게 두 가지 메모리 영역에 생성시킬 수 있다. stack과 heap 영역이다.

 

관련 설명을 간단히 가져오면 오래와 같다.

스택

매우 빠른 액세스

변수를 명시 적으로 할당 해제 할 필요가 없습니다.

공간은 CPU에 의해 효율적으로 관리되고 메모리는 단편화되지 않습니다.

지역 변수 만

스택 크기 제한 (OS에 따라 다름)

변수의 크기를 조정할 수 없습니다.

변수는 전역 적으로 액세스 할 수 있습니다.

메모리 크기 제한 없음

(상대적으로) 느린 액세스

효율적인 공간 사용을 보장하지 못하면 메모리 블록이 할당된 후 시간이 지남에 따라 메모리가 조각화되어 해제될 수 있습니다.

메모리를 관리해야 합니다 (변수를 할당하고 해제하는 책임이 있습니다)

변수는 C언어 realloc() or 자바 new

 

더욱 자세한 정보는 링크를 남겨둔다. 위의 지식이 필요하다 해당 내용을 보려 하면,

 

관련링크

 

스택(Stack)과 힙(Heap) 차이점

해당 Post는 스택(Stack)과 힙(Heap) 차이점를 정리한 파일이다.

junghyun100.github.io

스택과 힙의 대한 메모리 이야기는 어떤 특정 언어에 국한된 내용이 아니다. 

 

C#에서 객체와 메모리 적제

C#에서는 struct로 객체를 선언하고 만들면 스택에 생성이 된다. class로 객체를 선언하고 만들면 힙에 생성이 된다. 이는 다른 언어와는 큰 다른 특성이다. 다른 언어들(c, c++)등에서는 사용자가 객체를 생성하는 코드를 부를 때, 어떻게 생성하는가에 따라서 영역이 결정되지만, C#은 객체를 디자인할 때 이미 결정된다는 의미이다.

 

다음 코드를 보자.

struct structPoint 
{
    int x;
    int y;
}

class classPoint 
{
    public int x = 0;
    public int y = 0;
}

class Program
{
    static void Main(string[] args)
    {
        structPoint sp = new structPoint(); //a 
        classPoint cp = new classPoint();   //b

        structPoint sp1 = sp;  //c
        classPoint cp1 = cp;   //d
    }
}

a는 struct로 생성하는 코드이다. 이는 stack 영역에 생성이 된다.

b는 class를 생성하는 코드이다. 이는 heap 영역에 생성이 된다.

 좀 더 자세히 cp는 참조자이다. heap 영역에 존재하는 객체를 포인팅 하고 있다. 실제 값이 아니다. 이는 해당 변수들을 복사함에 있어 큰 차이를 가진다.

 

c는 struct 간의 복사이다. 값의 복사여서 실제 객체는 +1 (총 2개) 가 존재하게 된다.

d는 클래스의 참조일 뿐이다. 복사가 아니다. 실제 객체는 그대로 유지되고 참조 변수가 하나 더 생긴 것이다. cp1 또한 heap 영역에 존재하는 실제 값을 포인팅 할 뿐이다.

 

 

다음 코드에를 보자.

public struct structPoint 
{
    public int x;
    public int y;
}

class classPoint 
{
    public int x = 0;
    public int y = 0;
}

class Program
{
    static void Main(string[] args)
    {
        structPoint sp = new structPoint();
        classPoint cp = new classPoint();

        structPoint sp1 = sp;  
        classPoint cp1 = cp;   

        sp.x = 10;
        sp1.x = 11;

        Console.WriteLine(sp.x);
        Console.WriteLine(sp1.x);

        cp.x = 10;
        cp1.x = 11;

        Console.WriteLine(cp.x);
        Console.WriteLine(cp1.x);
    }
}

 struct에 접근하기 위해 코드를 좀 수정했다. 중요한 점은 각 변수들의 x값을 변경시켜 보았다는 것이다.

아래 출력 결과를 보자.

결과를 보자.

3번째의 결과 역시 11이다. 코드상으로는 Console.WriteLine(cp.x); 을 출력한 것이고, 위에서 10으로 초기화했는데?

이는 위에서 설명한 데로 cp1역 시 같은 값을 포인팅 하고 있다는 증명이다. cp, cp1은 같은 값을 참조시키고 있다.

 

이에 대한 이해는 중요하다. 직접 값을 바꿔가면서 이해를 해야 한다.

 

array 

 배열을 사용해 보자. struct 타입을 배열로 만들었을 때는, 값 타입일까 참조 타입일까?

structPoint[] arrsp = new structPoint[5];
structPoint[] arrsp1 = arrsp;

arrsp[0].x = 10;
arrsp1[0].x = 11;

Console.WriteLine(arrsp[0].x);
Console.WriteLine(arrsp1[0].x);

값타입? 참조타입?

결과출력

그냥 array는 참조 타입이다. array의 구성요소가 무엇인가가 중요한 것이 아니고, array 자체가 class 형태이다.

public abstract class Array : ICloneable, IList, ICollection, IEnumerable, IStructuralComparable, IStructuralEquatable

 

string

string str = "안녕하세요";
string str1 = str;
str1 = "Hello";

Console.WriteLine(str);
Console.WriteLine(str1);

위의 출력 결과는 어떻게 될까?

 

힌트는 string 은 class 다.

public sealed class String : IComparable, ICloneable, IConvertible, IEnumerable, IComparable<String>, IEnumerable<char>, IEquatable<String>

 

출력결과

문제가 없었으면 따로 적지 않았겠지. string은 class지만 정상 동작하지 않는데 왜일까?

str1 = "Hello";  //str1 = new string("Hello");

위의 문장은 우리가 생각하는 것처럼 대입으로 동작하지 않고, 뒤의 주석처럼 새로운 객체를 생성시키고 포인팅을 바꾼다. 지금은 요런 식으로 동작하는 것도 있구나 정도로만 알자.

 

value type, reference type

value type 에는 주로 struct, enum 형태, 수치 타입들이 대표적이다.

reference type 에는 class, interface, delegate 형태들, 그리고 .net 제공하는 많은 클래스 라이브러리 등이 주로 이 형태다.

 

코드로 타입을 알아보자.

structPoint sp = new structPoint();
classPoint cp = new classPoint();

Console.WriteLine(sp.GetType().IsValueType);
Console.WriteLine(cp.GetType().IsValueType);

다음과 같이 system.object가 제공하는 getType() 호출 뒤, IsValueType를 확인하면 해당 타입이 값 타입인지, 참조 타입인지 알 수 있다.

 

출력결과

sp는 true, 값 타입, cp는 false 참조 타입

 

배우고 익히자.

반응형

댓글