{ }codelper

배우기 › C#

언어마다 다른 것 · 36

구조체

관련된 값을 한 덩어리로 묶고, 그 덩어리가 복사되는지 공유되는지 알아 둬요.

이 단원은 C# · C++ · C에서 배워요.
Python·JavaScript·TypeScript·Java는 클래스와 객체로 자료를 묶고(클래스 단원), 크레이콘은 사전()으로 묶어요.
셋 다 대입하면 같은 객체를 함께 쓰는 참조라서, 복사되는 구조체와는 달라요.

점의 x·y, 학생의 이름·점수처럼 늘 같이 다니는 값은 구조체(struct)로 묶어요.
아래 예제는 점을 복사한 뒤 복사본만 바꿔 보고, 원본을 움직여서 복사와 원본의 차이를 보여 줘요.

using System; struct Point  // 값 타입: class로 바꾸면 q = p 가 같은 객체를 가리켜요{    public int X, Y;    public void Move(int dx, int dy) { X += dx; Y += dy; }    public int Len2() => X * X + Y * Y;    public override string ToString() => $"({X}, {Y})";} class Program{    static void Main()    {        string[] t = Console.ReadLine().Split();        var p = new Point { X = int.Parse(t[0]), Y = int.Parse(t[1]) };        Console.WriteLine($"p = {p}");         Point q = p;  // 데이터가 복사돼요        q.X = 10;        Console.WriteLine($"q = {q}");        Console.WriteLine($"p = {p}");         p.Move(1, 1);        Console.WriteLine($"p = {p}");        Console.WriteLine($"len2 = {p.Len2()}");    }}
stdin
3 4
stdout
p = (3, 4)
q = (10, 4)
p = (3, 4)
p = (4, 5)
len2 = 41

핵심

  • 묶음 하나가 새 타입이 돼요.
    변수 하나로 넘기고, 돌려주고, 배열에 담을 수 있어요.
  • 안에 든 값은 멤버(필드)라고 하고, p.x처럼 점으로 꺼내요.
  • 구조체를 대입하거나 함수에 넘기면 멤버가 통째로 복사돼요.
    그래서 q = p 다음에 q.x = 10을 해도 p는 (3, 4) 그대로예요.
  • 원본을 바꾸려면 포인터나 참조로 넘겨요.
    큰 구조체는 복사가 비싸서, 읽기만 할 때도 const 포인터·참조로 넘기곤 해요.

언어별로 쓰는 법

C#struct는 값 타입이라 Point q = p;가 데이터를 복사하고, class는 참조 타입이라 같은 객체를 가리켜요(예제를 class Point로 바꾸면 q.X = 10 뒤에 p도 (10, 4)) — int·double·DateTime도 구조체예요.
좌표·색·날짜처럼 작은 값은 struct(바뀌지 않게 하려면 readonly struct나 record struct), 그 밖에는 class.
원본을 넘길 땐 ref
C++typedef 없이 바로 Point.
안에 멤버 함수를 둘 수 있고(void move(int dx, int dy)), 바꾸지 않는 함수엔 int len2() const — struct와 class는 기본 공개 범위(struct는 public, class는 private)만 달라요.
원본은 Point&나 const Point&로 넘겨요
Cstruct Point { int x, y; };는 쓸 때마다 struct Point p;라고 적어야 해서 보통 typedef struct { int x, y; } Point; — 원본을 바꿀 땐 move(&p, 1, 1), 포인터로 멤버에 닿을 땐 (*p).x 대신 p->x, 읽기만 할 땐 const Point *

자주 하는 실수

목록 안 구조체의 멤버를 바꿨어요C#

var list = new List<Point> { new Point() };
list[0].X = 5;

→ 컴파일 오류 CS1612 — 인덱서가 복사본을 돌려줘요. 배열의 arr[0].X = 5는 되지만, foreach로 꺼낸 구조체의 멤버도 못 바꿔요(CS1654)

복사본을 꺼내 바꾼 뒤 다시 넣어요: var t = list[0]; t.X = 5; list[0] = t;

구조체를 ==로 비교했어요

if (p == q) { … }

→ C는 컴파일 오류예요. C++는 operator==가 없으면, C# struct는 ==를 정의하지 않으면(CS0019) 컴파일 오류예요

C는 멤버를 하나씩 비교해요(memcmp는 멤버 사이의 빈 패딩 바이트 때문에 틀릴 수 있어요).
C++20은 bool operator==(const Point&) const = default;, C#은 p.Equals(q)(멤버 값 비교)나 ==까지 만들어 주는 record struct.

값으로 받아서 복사본만 움직였어요C · C++

void move(Point p, int dx, int dy) {
    p.x += dx;
    p.y += dy;
}

→ 부른 쪽 p는 그대로예요. 오류도 경고도 없어요

C는 Point *, C++는 Point&로 받아요.

언어마다 다른 점
구조체를 넘기거나 대입할 때마다 복사인지 같은 것인지 따져 보세요.
C·C++·C# struct는 복사라서 원본을 바꾸려면 포인터(Point *)·참조(Point&)·C# ref를 써야 하고, C# class는 같은 객체를 함께 써요.

연습 문제