구조체
구조체(struct)
구조체는 서로 다른 타입 변수들을 하나의 묶음(자료형)으로 만드는 사용자 정의 타입이다.
struct Point {
int x;
int y;
};
struct Point p1;
p1.x = 10;
p1.y = 20;
Point
는int
멤버 두 개를 가지고 있는 구조체 타입이다.- 메모리상에는 연속된 공간에 저장됨
구조체 선언 방식
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번 요소의 주소 전달
- 구조체를 복사하지 않고 주소만 전달
- 성능 이점 및 원본 수정도 가능