본문스크랩 구조체


구조체의 구문 및 사용법을 제공하며 클래스와 구조체 간의 중요한 차이점에 대해서도 설명합니다.

 

첫째 예제에서는 구조체를 선언하고 사용하는 방법을 보여 주며, 둘째 예제에서는 인스턴스를 메서드로 전달할 때 구조체 및 클래스 간의 차이점에 대해 설명합니다. 또한 다음과 같은 항목에 대해 설명합니다.

  • 구조체 대 클래스
  • 힙 또는 스택?
  • 생성자 및 상속
  • 구조체의 특성

예제 1

다음 예제에서는 세 가지 멤버, 즉 속성, 메서드 및 전용 필드를 사용하여 구조체를 선언합니다. 여기서는 구조체의 인스턴스를 작성하여 이를 사용할 수 있도록 배치합니다.

// struct1.cs
using System;
struct SimpleStruct
{
    private int xval;
    public int X
    {
        get
        {
            return xval;
        }
        set
        {
            if (value < 100)
                xval = value;
        }
    }
    public void DisplayX()
    {
        Console.WriteLine("The stored value is: {0}", xval);
    }
}

class TestClass
{
    public static void Main()
    {
        SimpleStruct ss = new SimpleStruct();
        ss.X = 5;
        ss.DisplayX();
    }
}

출력

The stored value is: 5

 

구조체 vs 클레스

구조체는 클래스와 유사하게 보일 수 있으나 주의해야 하는 중요한 차이점이 있습니다. 우선, 클래스는 참조 형식이고 구조체는 값 형식입니다. 구조체를 사용하여 기본 제공 형식처럼 행동하는 개체를 만들 수 있으며 이러한 개체의 장점을 활용할 수 있습니다.

 

힙 or 스텍?

클래스에서 New 연산자를 호출하면 이 연산자는 힙에 할당됩니다. 그러나 구조체를 인스턴스화할 때는 스택에서 작성됩니다 이렇게 하면 성능이 향상되고 클래스에서처럼 구조체의 인스턴스에 대한 참조를 처리할 필요가 없습니다. 즉, 직접 구조체 인스턴스를 사용하여 작업할 수 있습니다. 이런 점 때문에 구조체를 메서드로 전달할 때 참조 대신 값으로 전달됩니다.

 

예제 2

다음은 구조체를 메서드로 전달할 때는 구조체의 복사본이 전달되나 클래스 인스턴스를 전달할 때는 참조가 전달되는 예제입니다.

// struct2.cs
using System;

class TheClass
{
    public int x;
}

struct TheStruct
{
    public int x;
}

class TestClass
{
    public static void structtaker(TheStruct s)
    {
        s.x = 5;
    }
    public static void classtaker(TheClass c)
    {
        c.x = 5;
    }
    public static void Main()
    {
        TheStruct a = new TheStruct();
        TheClass b = new TheClass();
        a.x = 1;
        b.x = 1;
        structtaker(a);
        classtaker(b);
        Console.WriteLine("a.x = {0}", a.x);
        Console.WriteLine("b.x = {0}", b.x);
    }
}

출력

a.x = 1
b.x = 5

 

코드 설명

 예제의 출력은 클래스 인스턴스가 classtaker 메서드로 전달될 때 클래스 필드의 값만이 변경되었음을 보여 줍니다. 그러나 구조체 필드는 인스턴스를 structtaker 메서드로 전달하여도 변경되지 않습니다. 이는 클래스에 대한 참조는 classtaker 메서드에 전달된 데 비해 구조체의 복사본은 structtaker 메서드에 전달되었기 때문입니다.

 

생성자 및 상속

구조체는 생성자를 선언할 수 있으나 반드시 매개 변수를 사용해야 합니다. 매개 변수 없이 구조체의 기본 생성자를 선언하면 오류가 발생합니다. 구조체 멤버는 초기 값을 가질 수 없습니다. 구조체 멤버를 기본값으로 초기화하기 위해 기본 생성자가 항상 제공되어야 합니다.

New연산자를 사용하여 구조체 개체를 생성할 경우 구조체 개체가 생성된 후에 적절한 생성자가 호출됩니다. 클래스와 달리 구조체에서는New연산자를 사용하지 않고 구조체를 인스턴스화할 수 있습니다. 하지만 New를 사용하지 않는 경우에는 필드가 할당되지 않은 상태로 남아 있게 되어 개체를 사용하려면 모든 필드를 초기화해야 합니다.

클래스의 경우와는 달리 구조체에 대한 상속은 없습니다. 구조체는 다른 구조체 또는 클래스에서 상속될 수 없으며, 클래스의 기본 클래스가 될 수도 없습니다. 그러나 구조체는 기본 클래스 개체에서 상속할 수 있습니다. 구조체는 인터페이스를 구현할 수 있으며 클래스와 똑같은 방법으로 구현합니다. 다음은 인터페이스를 구현하는 구조체에 대한 코드의 일부분입니다.

interface IImage
{
    void Paint();
}

struct Picture : IImage
{
    public void Paint()
    {
         // painting code goes here
    }
    private int x, y, z;  // other struct members
}

 

구조체의 특성

특성을 사용하여 구조체를 메모리에 배치하는 방법을 사용자 지정할 수 있습니다. 예를 들어,StructLayout(LayoutKind.Explicit)FieldOffset특성을 사용하여 C/C++의 공용 구조체를 작성할 수 있습니다.

using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Explicit)]
struct TestUnion
{
    [FieldOffset(0)]
    public int i;
    [FieldOffset(0)]
    public double d;
    [FieldOffset(0)]
    public char c;
    [FieldOffset(0)]
    public byte b1;
}

앞의 코드에서TestUnion의 모든 필드는 메모리의 동일한 위치에서 시작합니다.

다음 예제에서는 각 필드가 명시적으로 다르게 설정된 위치에서 시작합니다.

using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Explicit)]
struct TestExplicit
{
    [FieldOffset(0)]
    public long lg;
    [FieldOffset(0)]
    public int i1;
    [FieldOffset(4)]
    public int i2;
    [FieldOffset(8)]
    public double d;
    [FieldOffset(12)]
    public char c;
    [FieldOffset(14)]
    public byte b1;
}

두 개의int필드, 즉i1i2lg와 동일한 메모리 위치를 공유합니다. 이러한 종류의 구조체 배치 제어는 플랫폼 호출을 사용할 때 유용합니다.

 

결론

구조체는 간단히 사용할 수 있으며 때때로 유용한 경우가 많습니다. 구조체는 스택에서 작성되며 사용자는 구조체에 대한 참조가 아니라 구조체를 직접 처리한다는 점을 기억하십시오. 대개 데이터의 일부이며 자주 사용할 형식이 필요한 경우에는 구조체를 사용하는 것이 좋은 선택이 될 수 있습니다.

 

 


답글 남기기

이메일 주소는 공개되지 않습니다.