본문으로 건너뛰기

구조체

구조체(struct)

구조체는 서로 다른 타입 변수들을 하나의 묶음(자료형)으로 만드는 사용자 정의 타입이다.

struct Point {
int x;
int y;
};

struct Point p1;

p1.x = 10;
p1.y = 20;
  • Pointint 멤버 두 개를 가지고 있는 구조체 타입이다.
  • 메모리상에는 연속된 공간에 저장됨

구조체 선언 방식

struct Point p1; // 구조체 변수 선언
struct Point points[10]; // 구조체 배열 선언
struct Pont *ptr; // 구조체 포인터 선언

구조체 멈버 접근

  • 점(.) 연산자: 구조체 변수에서 멤버 접근
  • 화살표(->) 연산자: 구조체 포인터에서 멤버 접근
p1.x = 5;
ptr->y = 10;

typedef 키워드

typedef는 기존 타입에 새로운 별칭(alias)을 부여할 수 있게 해주는 키워드이다.

주로 구조체의 타입명을 간결하게 정의할 때 사용

typedef struct Point {
int x;
int y;
} Point;

Point p1; // struct Point의 별칭이 Point

익명 구조체 + typedef

typedef struct {
int x;
int y;
} Vector;
  • 구조체 이름 없이 typedef로 별칭만 부여도 가능
  • 구조체 이름을 외부에서 사용할 필요가 없을 때 사용됨

구조체의 메모리 구조

  • 구조체의 멤버는 선언 순서대로 메모리에 배치
  • 정렬(alignment)을 위해 패딩(padding)이 삽입될 수 있다.

정렬(alignment)과 패딩(padding)

메모리 정렬은 CPU의 효율적인 메모리 접근을 위해,
구조체 멤버를 특정 메모리 경계에서 시작하도록 강제하는 규칙
CPU는 특정 크기(4바이트, 8바이트 등)로 정렬된 메모리에서 읽고 쓰는 것이 더 빠르다.
정렬 기준은 보통 가장 큰 멤버의 자료형 크기에 따라 결정

struct Data {
char a;
int b;
};

printf("%zu", sizeof(struct Data)); // 8
  • char a는 1바이트이므로 메모리의 시작 주소에 저장
  • int b는 4바이트이므로 4의 배수 주소에서 시작되어야 함.
  • 따라서 a 다음에 3바이트의 패딩이 삽입되어 b는 4바이트 정렬을 유지함.
| a | - | - | - | b | b | b | b |

구조체 패딩 제어

패딩을 줄이기 위해선 멤버를 큰 타입에서 작은 타입 순서로 선언

struct Optimized {
int b;
short a;
char c;
char d;
};

printf("%zu", sizeof(struct Optimized)); // 8
  • int b는 4바이트
  • short a는 2바이트
  • char c, d는 1바이트
| b | b | b | b | a | a | c | d |
  • 구조체는 가장 큰 멤버의 정렬에 맞춰 전체 크기를 정렬한다.
  • 즉, 전체 sizeof(struct)도 그 정렬의 배수가 됨

그러나 멤버 변수의 순서는 정렬보다는, 가독성/논리적 의미에 초점을 맞추는 것이 좋다.

구조체 배열

typedef struct {
int id;
char name[20];
} Student;

// 구조체 요소 3개를 갖는 배열
Student students[3] = {
{1, "Alice"},
{2, "Bob"},
{3, "Charlie"},
};
  • students는 구조체를 요소로 갖는 배열
  • 각 요소는 독립적인 구조체 변수이며 . 연산자로 멤버에 접근 가능
printf("%s", students[1].name); // Bob

구조체 함수 인자 전달

구조체를 값으로 전달

void print_student(Student s) {
printf("hello, %s", s.name); // . 연산자로 접근
}

print_student(students[1]); // 인덱스 1번 요소의 값 전달
  • 구조체의 모든 멤버가 복사되어 전달
  • 복사 비용이 크고, 원본 수정 안됨

구조체 포인터로 전달(권장)

void print_student(Student* s) {
printf("hello, %s", s->name); // -> 연산자로 접근
}

print_student(&students[1]); // 인덱스 1번 요소의 주소 전달
  • 구조체를 복사하지 않고 주소만 전달
  • 성능 이점 및 원본 수정도 가능