Coding/C

[C언어] 구조체 멤버 정렬, 구조체 배열 - 코딩도장

GunP4ng 2024. 1. 22. 23:29

[코딩도장] Unit 51 ~ 53


1. 구조체 멤버 정렬

1. 구조체 멤버 정렬 사용하기 

컴퓨터 CPU 가 메모리에 접근할 때 32비트 CPU는 4바이트

64비트 CPU는 8바이트 단위로 접근한다

 

C언어 컴파일러는 CPU가 메모리의 데이터에 효율적으로 접근할 수 있도록

구조체를 일정한 크기로 정렬하게 된다

ex) 2, 4, 8, 16

 

※ 사진이나 네트워크 패킷같이 정렬을 하면 안되는 경우도 있다

 

struct PacketHeader {
    char flags;    // 1바이트
    int seq;       // 4바이트
};

가상 네트워크 패킷 구조체를 정의하고

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: 구조체 이름으로 크기 구하기

각 멤버의 크기와 구조체의 크기를 구한다

char 는 1바이트

int 는 4바이트

구조체 전체 크기는 8바이트이다

 

C언어에서는 구조체를 정렬할 때 멤버 중에서

가장 큰 자료형의 크기의 배수로 정렬한다

 

위에서는 int 가 가장 큰 자료형이기 때문에 4의 배수로 정렬된다

구조체 정렬

1바이트 크기의 char flags 뒤에는

4바이트를 맞추기 위해 3바이트가 더 들어간다

※ 구조체를 정렬할 때 남는 공간을 채우는 것을 패딩(padding)이라 한다

 

2. 구조체 정렬 크기 조절하기

Visual Studio, GCC 4.0 이상

#pragma pack(push, 정렬크기)
#pragma pack(pop)

 

GCC 4.0 미만

__attribute__((aligned(정렬크기), packed))

 

 

 

#pragma pack(push, 1)    // 1바이트 크기로 정렬
struct PacketHeader {
    char flags;    // 1바이트
    int seq;       // 4바이트
};
#pragma pack(pop)        // 정렬 설정을 이전 상태(기본값)로 되돌림

구조체를 정의할 때 pack 안의 1 을 넣어주면

1바이트 단위로 정렬하게 된다.

따라서 남는 공간 없이 자료형 크기 그대로 메모리에 올라가게 된다

1바이트 크기로 구조체 정렬

#pragma pack(pop) 을 사용하면 설정을 이전 상태로 되돌릴 수 있다

 

※ GCC 버전이 4.0 미만이라면 __attribute__((aligned(정렬크기), packed)) 를 사용한다

 

 

2. memset, memcpy 함수

1. memset 함수 사용하기

memset(구조체포인터, 설정할 값, sizeof(struct 구조체));

 

struct Point2D {
    int x;
    int y;
};

다음과 같이 구조체를 선언하고

 

struct Point2D p1;

memset(&p1, 0, sizeof(struct Point2D));		// p1을 구조체 크기만큼 0으로 설정

구조체 변수를 선언한 뒤

구조체의 내용을 모두 0으로 설정한다

구조체 변수를 0으로 설정하기

struct Point2D *p1 = malloc(sizeof(struct Point2D));

memset(p1, 0, sizeof(struct Point2D));		// p1을 구조체 크기만큼 0으로 설정

memset 함수로 메모리에 있는 값을 설정할 때는 포인터를 그대로 넣어준다

포인터는 안에 메모리 주소를 담고 있기 때문이다

메모리를 0으로 초기화하기

 

 

2. memcpy 함수 사용하기

memcpy(목적지포인터, 원본포인터, 크기)

void *memcpy(void *_Dst, void const *_Src, size_t_Size)

 

struct Point2D {
    int x;
    int y;
};

다음과 같이 구조체를 선언하고

 

struct Point2D p1;
struct Point2D p2;

p1.x = 10;    // p1의 멤버에만 값 저장
p1.y = 20;    // p1의 멤버에만 값 저장

memcpy(&p2, &p1, sizeof(struct Point2D));		// Point2D 구조체 크기만큼 p1의 내용을 p2로 복사

p1, p2 변수를 선언하고 p1 의 멤버에만 값을 저장한다

memcpy 함수를 사용하여 p1의 내용을 p2 까지 복사한다

 

&p1, &p2 같이 주소 연산자를 사용하여 변수의 메모리 주소를 구한다

memcpy 함수는 앞쪽이 목적지, 뒤쪽이 원본이다

구조체 변수에서 구조체 변수로 내용 복사

 

 

3. 구조체 배열

3. 구조체 배열 사용하기

struct Point2D {
    int x;
    int y;
};

다음과 같이 구조체를 선언한다

 

struct Point2D p[3];

[ ] 대괄호 안에는 크기를 넣어주면 된다

구조체 배열에서 각 요소에 접근하려면 . (점)을 사용하면 된다

구조체 배열

struct Point2D *p[3];

구조체 포인터 배열을 선언한다

포인터 배열을 선언하면 메모리를 할당한 후 사용해야 한다

 

for (int i = 0; i < sizeof(p) / sizeof(struct Point2D *); i++) }   // 요소 개수만큼 반복
    p[i] = malloc(sizeof(struct Point2D));    // 각 요소에 구조체 크기만큼 메모리 할당
}

배열 전체 크기 / 요소의 크기 를 해서 요소의 개수를 구한다

구조체 포인터 배열에서 각 요소에 접근하려면 -> 를 사용하여 멤버에 접근해야 한다

구조체 포인터 배열

※ 구조체 포인터 배열의 사용이 끝나면 반드시 메모리를 해제해야 한다

for (int i = 0; i < sizeof(p) / sizeof(struct Point2D *); i++) }  // 요소 개수만큼 반복
    free(p[i]);    // 각 요소의 동적 메모리 해제
}

4. 심사문제

51.6 암호화 헤더 크기 구하기

다음 소스 코드를 완성하여 12가 출력되게 만드세요. 구조체 멤버의 이름은 마음대로 지어도 됩니다.

정답에는 밑줄 친 부분에 들어갈 코드만 작성해야 합니다.

#include <stdio.h>

struct EncryptionHeader {
    char flags;
    ________
    ________
};
 
int main()
{
    struct EncryptionHeader header;
 
    printf("%d\n", sizeof(header));

    return 0;
}

 

정답

int num;
int encrypt;

51.7 패킷 크기 조절하기

다음 소스 코드를 완성하여 3이 출력되게 만드세요. 구조체 멤버의 이름은 마음대로 지어도 됩니다.

정답에는 밑줄 친 부분에 들어갈 코드만 작성해야 합니다.

#include <stdio.h>

____________________
____________________
____________________
____________________
____________________
____________________

int main()
{
    struct Packet pkt;

    printf("%d\n", sizeof(pkt));

    return 0;
}

 

정답

struct Packet {
    char net;
    char work;
    char num;
};

52.6 인적 정보 삭제하기

다음 소스 코드를 완성하여 아무 정보도 출력되지 않게 만드세요(함수 안의 인수만 채우면 됩니다.)

정답에는 밑줄 친 부분에 들어갈 코드만 작성해야 합니다.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>

struct Person {
    char name[20];
    int age;
    char address[100];
};

int main()
{
    struct Person p1;

    strcpy(p1.name, "홍길동");
    p1.age = 30;
    strcpy(p1.address, "서울시 용산구 한남동");

    memset(____________________________);

    printf("이름: %s\n", p1.name);
    printf("나이: %d\n", p1.age);
    printf("주소: %s\n", p1.address);

    return 0;
}

 

정답

&p1, 0, sizeof(struct Person)

52.7 인적 정보 복제하기

다음 소스 코드를 완성하여 인적 정보가 출력되게 만드세요(함수 안의 인수만 채우면 됩니다.)

정답에는 밑줄 친 부분에 들어갈 코드만 작성해야 합니다.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct Person {
    char name[20];
    int age;
    char address[100];
};

int main()
{
    struct Person *p1 = malloc(sizeof(struct Person));
    struct Person p2;

    strcpy(p1->name, "고길동");
    p1->age = 40;
    strcpy(p1->address, "서울시 서초구 반포동");

    memcpy(____________________________);

    printf("이름: %s\n", p2.name);
    printf("나이: %d\n", p2.age);
    printf("주소: %s\n", p2.address);

    free(p1);    

    return 0;

}

 

정답

&p2, p1, sizeof(struct Person)

53.6 선의 길이 구하기

표준 입력으로 x, y 좌표 4개가 입력되어 Point2D 구조체 배열에 저장됩니다. 여기서 점 4개는 첫 번째 점부터 마지막 점까지 순서대로 이어져 있습니다. 다음 소스 코드를 완성하여 첫 번째 점부터 마지막 점까지 연결된 선의 길이가 출력되게 만드세요.

정답에는 밑줄 친 부분에 들어갈 코드만 작성해야 합니다.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <math.h>

struct Point2D {
    int x;
    int y;
};

int main()
{
    struct Point2D p[4];
    double length = 0.0f;

    scanf("%d %d %d %d %d %d %d %d", 
        &p[0].x, &p[0].y, &p[1].x, &p[1].y,& p[2].x, &p[2].y, &p[3].x, &p[3].y
    );

    _______________________
    _______________________
    _______________________
    _______________________

    _______________________
    _______________________ 

    printf("%f\n", length);

    return 0;
}

 

정답

for (int i = 0; i < 3; i++) {
    double a = p[i].x - p[i + 1].x;
    double b = p[i].y - p[i + 1].y;

    length += sqrt((a * a) + (b * b));
}

53.7 나이가 가장 많은 사람 찾기

표준 입력으로 5명의 인적 정보가 입력됩니다(홀수 번째는 이름, 짝수 번째는 나이) 다음 소스 코드를 완성하여 입력된 사람 중에서 나이가 가장 많은 사람의 이름이 출력되게 만드세요.

정답에는 밑줄 친 부분에 들어갈 코드만 작성해야 합니다.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>

struct Person {
    char name[30];
    int age;
};

int main()
{
    struct Person *p[5];

    ____________________________
    ____________________________
    ____________________________
    ____________________________

    ____________________________
    ____________________________
    ____________________________    
    ____________________________
    ____________________________
    ____________________________
    ____________________________

    ____________________________
    ____________________________
    ____________________________    
    ____________________________
    ____________________________
    ____________________________
    ____________________________

    ____________________________

    for (int i = 0; i < sizeof(p) / sizeof(struct Person *); i++)
    {
        free(p[i]);
    }

    return 0;
}

 

정답

int max_age = 0;
int max_index = 0;
for (int i = 0; i < sizeof(p) / sizeof(struct Person *); i++) {
    p[i] = malloc(sizeof(struct Person));

    scanf("%s %d", p[i]->name, &p[i]->age);
        
    if (p[i]->age > max_age) {
        max_age = p[i]->age;
        max_index = i;
    }
}
printf("%s", p[max_index]->name);

가장 많은 나이를 저장할 max_age 변수를 선언하고

max_age 의 인덱스를 저장할 max_index 를 선언한다

 

요소의 개수 만큼 for 를 이용하여 반복하여 메모리를 할당한다

scanf 로 요소의 개수만큼 이름과 나이를 입력받는다

 

만약 p[i]->age 가 max_age 보다 크다면

max_age 를 p[i]->age 로 정하고

max_index 를 현재 인덱스 ( i ) 로 지정한다

 

마지막으로 p[max_index]->name  을 출력하면 된다