Computer >> 컴퓨터 >  >> 프로그래밍 >> C 프로그래밍

C 언어 구조체의 가변 배열 멤버(Flexible Array Member) 완벽 정리

가변 배열 멤버(Flexible Array Member)란?

C 언어에서 가변 배열 멤버란 구조체(structure) 내부에 배열의 크기를 지정하지 않은 채 선언하는 배열을 말합니다. 이렇게 선언된 배열은 컴파일 시점에 크기가 고정되지 않고, 실행 중 필요한 만큼 메모리를 할당해 사용할 수 있어 크기가 '유연한(flexible)' 성격을 가집니다.

단, 가변 배열 멤버는 반드시 구조체의 마지막 멤버로 선언되어야 합니다. 이는 컴파일러가 배열 시작 위치 뒤쪽에 필요한 만큼의 연속된 메모리를 붙여 할당할 수 있도록 하기 위한 필수 조건입니다.

사용 규칙

  • 가변 배열 멤버는 구조체(클래스)의 마지막 멤버여야 합니다.
  • 구조체에는 가변 배열 멤버 외에 최소 하나 이상의 다른 명명된 멤버가 존재해야 합니다.
  • 실제 메모리는 일반적으로 sizeof(구조체) + 배열 크기만큼 malloc()으로 동적 할당합니다.

예제 코드

아래 예제는 직원 정보를 저장하는 구조체에 가변 배열 멤버를 활용해, 이름 길이에 맞춰 메모리를 동적으로 할당하는 방법을 보여줍니다.

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

// employee 타입의 구조체.
// 가변 배열 멤버 외에 최소 하나 이상의 명명된 멤버를 포함해야 합니다.
struct employee
{
    int emp_id;
    int name_len;
    int emp_size; // 'emp_size' 변수는 가변 문자 배열 emp_name[]의 크기를 저장하는 데 사용됩니다.
    char emp_name[]; // 가변 배열 멤버 emp_name[]은 반드시 구조체의 마지막 멤버여야 합니다.
};

struct employee *createEmployee(struct employee *e, int id, char a[])
{
    // 구조체 크기 + 이름 길이만큼 메모리 할당
    e = (struct employee *)malloc(sizeof(*e) + sizeof(char) * strlen(a));
    e->emp_id = id;
    e->name_len = strlen(a);
    // 사용자가 전달한 배열 a[]의 크기에 맞게 emp_name에 복사
    strcpy(e->emp_name, a);
    return e;
}

void printEmployee(struct employee *e) // 직원 정보를 출력하는 함수
{
    printf("Employee_id : %d\n" "Employee Name : %s\n" "Name Length: %d\n\n", e->emp_id, e->emp_name, e->name_len);
}

int main()
{
    struct employee *e1 = createEmployee(e1, 26, "Ram");
    struct employee *e2 = createEmployee(e2, 53, "Madhu");
    printEmployee(e1);
    printEmployee(e2);
    printf("Size of structure Employee: %lu\n", sizeof(struct employee));
    return 0;
}

실행 결과

Employee_id : 26
Employee Name : Ram
Name Length: 3
Employee_id : 53
Employee Name : Madhu
Name Length: 5
Size of structure Employee: 12

결과 분석

실행 결과에서 주목할 부분은 구조체 자체의 크기가 12바이트라는 점입니다. 이는 int형 멤버 세 개(emp_id, name_len, emp_size)의 크기(4바이트 × 3)에 해당하며, 가변 배열 멤버 emp_name[]은 구조체 크기 계산에 포함되지 않습니다.

대신 createEmployee() 함수에서 malloc()을 호출할 때 sizeof(*e) + sizeof(char) * strlen(a)를 사용해 구조체 크기에 이름 길이만큼의 추가 공간을 함께 할당합니다. 덕분에 "Ram"(3글자)과 "Madhu"(5글자)처럼 길이가 서로 다른 문자열도 하나의 구조체 정의만으로 손쉽게 처리할 수 있습니다.

가변 배열 멤버의 장점

  • 메모리 효율성: 데이터 크기에 딱 맞게 할당하므로 공간 낭비가 없습니다.
  • 데이터 지역성: 구조체 헤더와 배열 데이터가 연속된 메모리에 위치해 캐시 효율이 좋습니다.
  • 관리 편의성: 별도의 포인터와 추가 할당 없이 한 번의 malloc() 호출로 처리할 수 있습니다.