csapp 3장 오늘은 시마이 칠 생각이다
이 재미도 없는 걸 언제까지 붙들기는 싫으니 말이다
C언어에서 타입끼리 결합해 데이터 타입을 만드는 2가지 방법이 있다
구조체: struct
키워드를 사용해 여러 객체를 하나의 단위로 묶는다
공용체: union
키워드로 하나의 객체를 다른 여러 타입으로 참조가능케 한다
구현 방식은 배열과 유사하다고 한다
모든 구성 요소는 연속적인 메모리 영역에 저장
구조체 포인터는 구조체의 첫 번째 바이트 주소를 가리킴
컴파일러는 각 구조체 타입에 대해 각 필드의 바이트 오프셋1 정보를 유지
struct rec {
int i;
int j;
int a[2];
int *p;
};
int i
→ 4바이트
int j
→ 4바이트
int a[2]
→ 8바이트 (int 2개)
int *p
→ 8바이트 (포인터)
총 크기 = 24바이트
Offset | 0 | 4 | 8 | 12 | 16 |
---|---|---|---|---|---|
Field | i | j | a[0] | a[1] | p |
오프셋 값은 구조체 시작 주소로부터 각 필드의 떨어진 거리
컴파일러는 구조체 주소에 필드 오프셋 더해 구조체 필드에 접근한다
필드의 오프셋을 구조체 주소에 더하면 해당 필드의 주소를 얻을 수 있다
굳이 예시는 안들겠다
사실 당연한 내용이라
구조체의 필드 선택은 전적으로 컴파일 시점에 처리
생성된 기계어 코드에는 필드 선언이나 이름 정보가 전혀 포함되지 않는다
필드 접근은 구조체 시작 주소 + 오프셋 방식으로 처리
공용체(union)는 C언어 타입 시스템 우회란다
하나의 객체를 여러 타입으로 참조 가능케 말이다
선언 문법은 같지만 의미는 다르다
구조체(struct)는 각 필드가 서로 다른 메모리 영역
공용체(union)는 모든 필드가 같은 메모리 영역
struct S3 {
char c;
int i[2];
double v;
};
union U3 {
char c;
int i[2];
double v;
};
타입 | c | i | v | 크기 |
---|---|---|---|---|
S3 | 0 | 4 | 16 | 24 |
U3 | 0 | 0 | 0 | 8 |
각 필드의 오프셋과 전체 크기
※ S3
에서 i
가 오프셋 4이고, v
가 오프셋 16인 이유느 패딩 때문이다
p->c
, p->i[0]
, p->v
모두 같은 시작 주소 참조
공용체 전체 크기는 가장 큰 필드 크기
공용체를 이용하면 메모리를 절약 가능하다
구조체는 모든 필드가 각자 메모리 공간을 가지기에 동시에 쓰지 않더라도 항상 공간 차지
공용체는 모든 필드가 같이 메모리 사용해서 한 시점에 하나의 필드만 사용
단, 필드들이 동시에 쓰이지 않는 경우여야 안전
구조체는 필드 크기 합계 + 패딩이 전체 크기
공용체는 가장 큰 필드의 크기가 전체 크기
따라서 크기 큰 필드를 공유하여 전체 크기 감소
이진 트리 노드처럼 리프 노드와 내부 노드가 구조가 다른 경우
구조체: 리프/내부 관계없이 모든 필드에 공간 할당 → 낭비 발생
공용체: 리프용 데이터와 내부 노드용 포인터가 같은 공간을 공유 → 메모리 절반 수준으로 줄어듦
기본 데이터 타입이 저장될 수 있는 주소에 제한이 있다
어떤 객체의 주소가 특정값 K(일반적으로 2, 4, 8)의 배수가 되어야 한다는 규칙이다
x86-64에서의 경우를 예시로 보여준다
데이터 정렬 안 맞아도 작동은 하나 맞는게 성능에 좋아 권장한다 한다
K 바이트 크기의 기본 객체 -> 주소는 K의 배수여야 함
K 값 | 타입 |
---|---|
1 | char |
2 | short |
4 | int, float |
8 | long, double, char * |
데이터 배치 시, 각 객체가 정렬 규칙 만족하도록 구성
전역 데이터는 컴파일러가 .align
지시어로 강제 정렬
.align 8
이후 데이터의 시작 주소가 8의 배수구조체도 패딩2 등으로 규칙을 지킨다고 한다