📖UNIT 51.
📒 구조체 크기 알아보기
구조체의 크기를 알아보자. 어떻게 계산하면 될까?
sizeof 함수를 사용하도록 하자.
우선 가상의 네트워크 헤드를 구조체로 만들었다.
#include <stdio.h>
struct PacketHeader {
char flags; // 1바이트
int seq; // 4바이트
};
int main()
{
struct PacketHeader header;
printf("%d\n", sizeof(header.flags)); // 1: char는 1바이트
printf("%d\n", sizeof(header.seq)); // 4: int는 4바이트
printf("%d\n", sizeof(header)); // 8: 구조체 전체 크기는 8바이트
printf("%d\n", sizeof(struct PacketHeader)); // 8: 구조체 이름으로 크기 구하기
return 0;
}
PacketHeader 구조체 내에는 flags와 seq가 존재한다. 각각 char, int 로 선언되었기에 1바이트 4바이트를 차지한다.
그럼 당연히 header나 struct PacketHeader의 크키는 5 바이트가 나와주어야 한다.
하지만 8바이트가 되어있다................ ??
그 이유는 구조체를 정렬할 때 가장 큰 자료형의 크기의 배수로 정렬하기 때문이다.
즉, 여기서 가장 큰 자료형은 4바이트이다. 딱 필요한 바이트는 5바이트지만, 8바이트가 사용되는 것이다.
그래서 남은 3바이트는 패딩을 하게 된다.
그래서 이런 모습을 띄게 된다. 그럼 저 공간은 무슨 값으로 채워질까 ?
📒 구조체 정렬 크기 조정하기
뭐 경우에 따라서 구조체 정렬을 피해야 할 수도 있다.
그런 경우에는 어떻게 하면 될까?
우선 한가지 알아야 할 것이, 정렬되는 사이즈를 정하지 않는다면, 자동으로 가장 큰 자료형의 배수로 정렬된다는 것이다.
만약 그 정령되는 단위가 1이라면 어떻게 될까.
당연히 패딩되는 공간은 생기지 않을 것이다.
#include <stdio.h>
#pragma pack(push, 1) // 1바이트 크기로 정렬
struct PacketHeader {
char flags; // 1바이트
int seq; // 4바이트
};
#pragma pack(pop) // 정렬 설정을 이전 상태(기본값)로 되돌림
int main()
{
struct PacketHeader header;
printf("%d\n", sizeof(header.flags)); // 1: char는 1바이트
printf("%d\n", sizeof(header.seq)); // 4: int는 4바이트
printf("%d\n", sizeof(header)); // 5: 1바이트 단위로 정렬했으므로
// 구조체 전체 크기는 5바이트
return 0;
}
이렇게 header의 사이즈가 5바이트로 결정된다.
만약 단위가 3이라면 어떻게 될까.
이렇게 1바이트 만큼 패딩을 해주는 것을 알 수 있다.
📖UNIT 52.
📒 구조체와 메모리를 0으로 설정하기
먼저 구조체와 메모리를 간단하게 0으로 설정해보자.
구조체도 결국 변수처럼 사용하게 된다면, 메모리를 사용하기에 메모리 관련 함수들을 사용할 수 있다.
구조체 변수나 메모리의 값을 한 번에 설정하고 싶다면 memset 함수를 사용하면 된다.
#include <stdio.h>
#include <string.h> // memset 함수가 선언된 헤더 파일
struct Point2D {
int x;
int y;
};
int main()
{
struct Point2D p1;
memset(&p1, 0, sizeof(struct Point2D)); // p1을 구조체 크기만큼 0으로 설정
printf("%d %d\n", p1.x, p1.y); // 0 0: memset을 사용하여 0으로 설정했으므로
// x, y 모두 0
return 0;
}
이런 실행결과를 얻을 수 있다.
결국 둘 다 0으로 초기화 된 것이다.
이런 메모리 구조를 띄고 있는 상황이다.
memset함수를 사용할 때는 첫 번째 인자에 무조건 주소를 넣어주어야 한다.
📒 구조체와 메모리 복사하기
이번엔 구조체 변수를 p1, p2를 선언하고 p1의 값들을 p2로 옮겨보도록 하자.
옮길 때는 memcpy 함수를 사용하면 된다. 말 그대로 memory copy 이다.
#include <stdio.h>
#include <string.h> // memcpy 함수가 선언된 헤더 파일
struct Point2D {
int x;
int y;
};
int main()
{
struct Point2D p1;
struct Point2D p2;
p1.x = 10; // p1의 멤버에만 값 저장
p1.y = 20; // p1의 멤버에만 값 저장
memcpy(&p2, &p1, sizeof(struct Point2D)); // Point2D 구조체 크기만큼 p1의 내용을 p2로 복사
printf("%d %d\n", p2.x, p2.y); // 10 20: p1의 내용을 p2로 복사했으므로 10 20
return 0;
}
이렇게 해주면 된다.
동적으로 할당된 구조체여도 마찬가지이다. 하지만 조심할 것은,
#include <stdio.h>
#include <stdlib.h> // malloc, free 함수가 선언된 헤더 파일
#include <string.h> // memcpy 함수가 선언된 헤더 파일
struct Point2D {
int x;
int y;
};
int main()
{
struct Point2D *p1 = malloc(sizeof(struct Point2D));
struct Point2D *p2 = malloc(sizeof(struct Point2D));
p1->x = 10; // p1의 멤버에만 값 저장
p1->y = 20; // p1의 멤버에만 값 저장
memcpy(p2, p1, sizeof(struct Point2D)); // Point2D 구조체 크기만큼 p1의 내용을 p2로 복사
printf("%d %d\n", p2->x, p2->y); // 10 20: p1의 내용을 p2로 복사했으므로 10 20
free(p2);
free(p1);
return 0;
}
p1.x, p2.x 로 쓰는 것이 아니라 p1->x p2->x로 써야 한다는 것이다.
📖UNIT 53.
구조체로도 배열을 만들 수 있을까 ?
당연히 만들 수 있다.
#include <stdio.h>
struct Point2D {
int x;
int y;
};
int main()
{
struct Point2D p[3]; // 크기가 3인 구조체 배열 생성
p[0].x = 10; // 인덱스로 요소에 접근한 뒤 점으로 멤버에 접근
p[0].y = 20;
p[1].x = 30;
p[1].y = 40;
p[2].x = 50;
p[2].y = 60;
printf("%d %d\n", p[0].x, p[0].y); // 10 20
printf("%d %d\n", p[1].x, p[1].y); // 30 40
printf("%d %d\n", p[2].x, p[2].y); // 50 60
return 0;
}
이렇게 코드를 짰을 때 생성되는 메모리를 확인해보자.
#include <stdio.h>
struct Point2D {
int x;
int y;
};
int main()
{
// 구조체 배열을 선언하면서 초기화
struct Point2D p1[3] = { { .x = 10, .y = 20 }, { .x = 30, .y = 40 }, { .x = 50, .y = 60 } };
printf("%d %d\n", p1[0].x, p1[0].y); // 10 20
printf("%d %d\n", p1[1].x, p1[1].y); // 30 40
printf("%d %d\n", p1[2].x, p1[2].y); // 50 60
// 구조체 배열을 선언하면서 초기화
struct Point2D p2[3] = { { 10, 20 }, { 30, 40 }, { 50, 60 } };
printf("%d %d\n", p2[0].x, p2[0].y); // 10 20
printf("%d %d\n", p2[1].x, p2[1].y); // 30 40
printf("%d %d\n", p2[2].x, p2[2].y); // 50 60
return 0;
}
이렇게 구조체 배열을 선언과 동시에 초기화 해주는 방법도 있으니 참고하도록 하자.
이 상황에서 p1과 p2의 크기는 몇이 나올까.
사이즈는 당연히 24바이트가 나와준다.
📒 구조체 포인터 배열 선언
이번엔 구조체 포인터 배열을 선언하도록 해보자.
항상 조심할 점은, 구조체 포인터 배열은 -> 연산자를 사용한다는 것이다.
#include <stdio.h>
#include <stdlib.h> // malloc, free 함수가 선언된 헤더 파일
struct Point2D {
int x;
int y;
};
int main()
{
struct Point2D *p[3]; // 크기가 3인 구조체 포인터 배열 선언
// 구조체 포인터 배열 전체 크기에서 요소(구조체 포인터)의 크기로 나눠서 요소 개수를 구함
for (int i = 0; i < sizeof(p) / sizeof(struct Point2D *); i++) // 요소 개수만큼 반복
{
p[i] = malloc(sizeof(struct Point2D)); // 각 요소에 구조체 크기만큼 메모리 할당
}
p[0]->x = 10; // 인덱스로 요소에 접근한 뒤 화살표 연산자로 멤버에 접근
p[0]->y = 20;
p[1]->x = 30;
p[1]->y = 40;
p[2]->x = 50;
p[2]->y = 60;
printf("%d %d\n", p[0]->x, p[0]->y); // 10 20
printf("%d %d\n", p[1]->x, p[1]->y); // 30 40
printf("%d %d\n", p[2]->x, p[2]->y); // 50 60
for (int i = 0; i < sizeof(p) / sizeof(struct Point2D *); i++) // 요소 개수만큼 반복
{
free(p[i]); // 각 요소의 동적 메모리 해제
}
return 0;
}
그래서 이렇게 코딩을 해볼 수 있겠다.
간단하게 내가 이해한 바로는, 먼저 크기가 3인 구조체 포인터 배열을 선언해야 한다.
이부분에 대한 이해는 구조체 포인터를 좀 더 완벽히 이해하고 다시 진행해야 할 것 같다.
'스터디 그룹 > ProjectH4C' 카테고리의 다른 글
ProjectH4C 3개월 3주차 과제 (pwnable.kr - bof) (0) | 2021.03.31 |
---|---|
ProjectH4C 3개월 3주차 과제 (pwnable.kr - fd) (0) | 2021.03.30 |
ProjectH4C 3개월 2주차 과제 (해커스쿨 ftz - 8) (0) | 2021.03.28 |
ProjectH4C 3개월 2주차 과제 (해커스쿨 ftz - 7) - 스킵 (0) | 2021.03.28 |
ProjectH4C 3개월 2주차 과제 (해커스쿨 ftz - 6) (0) | 2021.03.28 |