Namespaces
Variants

Struct declaration

From cppreference.net

구조체는 멤버들의 저장 공간이 순서대로 할당되는 타입입니다 (반면 공용체는 멤버들의 저장 공간이 겹쳐지는 타입입니다).

구조체의 타입 지정자 는 사용된 키워드를 제외하고 union 타입 지정자와 동일합니다:

목차

구문

struct attr-spec-seq  (선택 사항) name  (선택 사항) { struct-declaration-list } (1)
struct attr-spec-seq  (선택 사항) name (2)
1) 구조체 정의: 새로운 타입 struct name 을 소개하고 그 의미를 정의함
2) 단독으로 한 줄에 사용될 경우, struct name ; 와 같이, 선언 하지만 struct name 을 정의하지는 않습니다(아래의 전방 선언 참조). 다른 상황에서는 이전에 선언된 struct를 지칭하며, attr-spec-seq 는 허용되지 않습니다.
name - 정의 중인 구조체의 이름
struct-declaration-list - 임의의 수의 변수 선언, bit-field 선언, 그리고 static assert 선언. 불완전한 타입의 멤버와 함수 타입의 멤버는 허용되지 않음 (아래 설명된 flexible array member는 제외)
attr-spec-seq - (C23) 선택적 attributes 목록, 구조체 타입에 적용됨

설명

구조체 객체 내에서, 그 요소들의 주소(그리고 비트 필드 할당 단위의 주소)는 멤버들이 정의된 순서대로 증가합니다. 구조체에 대한 포인터는 그 첫 번째 멤버에 대한 포인터로 캐스트될 수 있습니다(또는 멤버가 비트 필드인 경우, 그 할당 단위로 캐스트될 수 있습니다). 마찬가지로, 구조체의 첫 번째 멤버에 대한 포인터는 이를 포함하는 구조체에 대한 포인터로 캐스트될 수 있습니다. 구조체의 어떤 두 멤버 사이나 마지막 멤버 뒤에는 이름 없는 패딩이 있을 수 있지만, 첫 번째 멤버 앞에는 있을 수 없습니다. 구조체의 크기는 적어도 그 멤버들의 크기 합만큼은 됩니다.

구조체가 하나 이상의 이름 있는 멤버를 정의하는 경우, 마지막 멤버를 불완전 배열 타입으로 추가 선언할 수 있습니다. 유연 배열 멤버의 요소에 접근할 때(연산자 . 또는 -> 를 사용하고 유연 배열 멤버의 이름을 오른쪽 피연산자로 사용하는 표현식에서), 구조체는 해당 객체에 할당된 메모리에 맞는 가장 큰 크기로 배열 멤버가 있는 것처럼 동작합니다. 추가 저장 공간이 할당되지 않은 경우, 1개 요소를 가진 배열처럼 동작하지만 해당 요소에 접근하거나 그 요소를 지난 포인터를 생성할 때의 동작은 정의되지 않습니다. 초기화와 대입 연산자는 유연 배열 멤버를 무시합니다. sizeof 는 이를 생략하지만, 생략으로 인해 암시되는 것보다 더 많은 후행 패딩을 가질 수 있습니다. 유연 배열 멤버를 가진 구조체(또는 유연 배열 멤버를 가진 재귀적 가능성 구조체 멤버를 가진 공용체)는 배열 요소나 다른 구조체의 멤버로 나타날 수 없습니다.

struct s { int n; double d[]; }; // s.d is a flexible array member
struct s t1 = { 0 };          // OK, d is as if double d[1], but UB to access
struct s t2 = { 1, { 4.2 } }; // error: initialization ignores flexible array
// if sizeof (double) == 8
struct s *s1 = malloc(sizeof (struct s) + 64); // as if d was double d[8]
struct s *s2 = malloc(sizeof (struct s) + 40); // as if d was double d[5]
s1 = malloc(sizeof (struct s) + 10); // now as if d was double d[1]. Two bytes excess.
double *dp = &(s1->d[0]);    // OK
*dp = 42;                    // OK
s1->d[1]++;                  // Undefined behavior. 2 excess bytes can't be accessed
                             // as double.
s2 = malloc(sizeof (struct s) + 6);  // same, but UB to access because 2 bytes are
                                     // missing to complete 1 double
dp = &(s2->d[0]);            // OK, can take address just fine
*dp = 42;                    // undefined behavior
*s1 = *s2; // only copies s.n, not any element of s.d
           // except those caught in sizeof (struct s)
(C99부터)

union와 유사하게, 구조체의 이름 없는 멤버 중 그 타입이 이름 없는 구조체인 것을 익명 구조체(anonymous struct) 라고 합니다. 익명 구조체의 모든 멤버는 자신을 포함하는 구조체나 union의 멤버로 간주되며, 구조체 레이아웃을 유지합니다. 이를 포함하는 구조체나 union 또한 익명인 경우 이 규칙은 재귀적으로 적용됩니다.

struct v
{
   union // anonymous union
   {
      struct { int i, j; }; // anonymous structure
      struct { long k, l; } w;
   };
   int m;
} v1;
v1.i = 2;   // valid
v1.k = 3;   // invalid: inner structure is not anonymous
v1.w.k = 5; // valid

union와 유사하게, 구조체가 (익명 중첩 구조체나 union을 통해 얻은 멤버들을 포함하여) 이름 있는 멤버 없이 정의된 경우 프로그램의 동작은 정의되지 않습니다.

(since C11)

전방 선언

다음 형식의 선언

struct attr-spec-seq  (선택적) name ;

이전에 선언된 태그 이름 공간 내 name 의 의미를 숨기고, 현재 스코프에서 나중에 정의될 새로운 struct 이름으로 name 을 선언합니다. 정의가 나타날 때까지 이 struct 이름은 incomplete type 을 가집니다.

이렇게 하면 서로를 참조하는 구조체를 허용합니다:

struct y;
struct x { struct y *p; /* ... */ };
struct y { struct x *q; /* ... */ };

새로운 구조체 이름은 다른 선언 내에서 구조체 태그를 사용하여 도입될 수도 있지만, 동일한 이름을 가진 구조체가 태그 name space 에 이전에 선언된 경우, 해당 태그는 그 이름을 참조하게 됩니다

struct s* p = NULL; // 알려지지 않은 구조체를 가리키는 태그는 이를 선언함
struct s { int a; }; // p가 가리키는 구조체의 정의
void g(void)
{
    struct s; // 새로운 지역 구조체 s의 전방 선언
              // 이는 이 블록의 끝까지 전역 구조체 s를 가림
    struct s *p;  // 지역 구조체 s를 가리키는 포인터
                  // 위의 전방 선언이 없었다면,
                  // 이는 파일 범위의 s를 가리켰을 것임
    struct s { char* p; }; // 지역 구조체 s의 정의
}

키워드

struct

참고 사항

구조체 초기화 규칙에 대해서는 구조체 초기화 를 참조하십시오.

불완전한 타입의 멤버는 허용되지 않으며, 구조체 타입은 정의가 끝날 때까지 완전하지 않기 때문에, 구조체는 자신의 타입을 멤버로 가질 수 없습니다. 자신의 타입에 대한 포인터는 허용되며, 일반적으로 연결 리스트나 트리의 노드를 구현하는 데 사용됩니다.

구조체 선언은 scope 를 설정하지 않기 때문에, struct-declaration-list 내의 선언으로 도입된 중첩 타입, 열거형 및 열거자는 구조체가 정의된 주변 scope에서 보입니다.

예제

#include <stddef.h>
#include <stdio.h>
int main(void)
{
    // 구조체 타입 선언
    struct car
    {
        char* make;
        int year;
    };
    // 이전에 선언된 구조체 타입의 객체 선언 및 초기화
    struct car c = {.year = 1923, .make = "Nash"};
    printf("1) Car: %d %s\n", c.year, c.make);
    // 구조체 타입, 해당 타입의 객체, 그리고 이를 가리키는 포인터 선언
    struct spaceship
    {
        char* model;
        int max_speed;
    } ship = {"T-65 X-wing starfighter", 1050},
    *pship = &ship;
    printf("2) Spaceship: %s. Max speed: %d km/h\n\n", ship.model, ship.max_speed);
    // 주소는 정의 순서대로 증가. 패딩이 삽입될 수 있음
    struct A { char a; double b; char c; };
    printf(
        "3) Offset of char a = %zu\n"
        "4) Offset of double b = %zu\n"
        "5) Offset of char c = %zu\n"
        "6) Size of struct A = %zu\n\n",
        offsetof(struct A, a),
        offsetof(struct A, b),
        offsetof(struct A, c),
        sizeof(struct A)
    );
    struct B { char a; char b; double c; };
    printf(
        "7) Offset of char a = %zu\n"
        "8) Offset of char b = %zu\n"
        "9) Offset of double c = %zu\n"
        "A) Size of struct B = %zu\n\n",
        offsetof(struct B, a),
        offsetof(struct B, b),
        offsetof(struct B, c),
        sizeof(struct B)
    );
    // 구조체를 가리키는 포인터는 첫 번째 멤버를 가리키는 포인터로 캐스팅될 수 있으며 그 반대도 가능
    char** pmodel = (char **)pship;
    printf("B) %s\n", *pmodel);
    pship = (struct spaceship *)pmodel;
}

가능한 출력:

1) Car: 1923 Nash
2) Spaceship: T-65 X-wing starfighter. Max speed: 1050 km/h
3) Offset of char a = 0
4) Offset of double b = 8
5) Offset of char c = 16
6) Size of struct A = 24
7) Offset of char a = 0
8) Offset of char b = 1
9) Offset of double c = 8
A) Size of struct B = 16
B) T-65 X-wing starfighter

결함 보고서

다음의 동작 변경 결함 보고서들은 이전에 발표된 C 표준에 소급 적용되었습니다.

DR 적용 대상 게시된 동작 올바른 동작
DR 499 C11 익명 구조체/공용체의 멤버들이 포함하는 구조체/공용체의 멤버로 간주됨 해당 멤버들은 자신의 메모리 레이아웃을 유지함

참고문헌

  • C23 표준 (ISO/IEC 9899:2024):
  • 6.7.2.1 구조체 및 공용체 지정자 (p: TBD)
  • C17 표준 (ISO/IEC 9899:2018):
  • 6.7.2.1 구조체 및 공용체 지정자 (p: 81-84)
  • C11 표준 (ISO/IEC 9899:2011):
  • 6.7.2.1 구조체 및 공용체 지정자 (p: 112-117)
  • C99 표준 (ISO/IEC 9899:1999):
  • 6.7.2.1 구조체 및 공용체 지정자 (p: 101-104)
  • C89/C90 표준 (ISO/IEC 9899:1990):
  • 3.5.2.1 구조체 및 공용체 지정자

참고 항목

C++ 문서 에서 클래스 선언 참조