Struct declaration
구조체는 멤버들의 저장 공간이 순서대로 할당되는 타입입니다 (반면 공용체는 멤버들의 저장 공간이 겹쳐지는 타입입니다).
구조체의
타입 지정자
는 사용된 키워드를 제외하고
union
타입 지정자와 동일합니다:
목차 |
구문
struct
attr-spec-seq
(선택 사항)
name
(선택 사항)
{
struct-declaration-list
}
|
(1) | ||||||||
struct
attr-spec-seq
(선택 사항)
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 목록, 구조체 타입에 적용됨 |
설명
구조체 객체 내에서, 그 요소들의 주소(그리고 비트 필드 할당 단위의 주소)는 멤버들이 정의된 순서대로 증가합니다. 구조체에 대한 포인터는 그 첫 번째 멤버에 대한 포인터로 캐스트될 수 있습니다(또는 멤버가 비트 필드인 경우, 그 할당 단위로 캐스트될 수 있습니다). 마찬가지로, 구조체의 첫 번째 멤버에 대한 포인터는 이를 포함하는 구조체에 대한 포인터로 캐스트될 수 있습니다. 구조체의 어떤 두 멤버 사이나 마지막 멤버 뒤에는 이름 없는 패딩이 있을 수 있지만, 첫 번째 멤버 앞에는 있을 수 없습니다. 구조체의 크기는 적어도 그 멤버들의 크기 합만큼은 됩니다.
|
구조체가 하나 이상의 이름 있는 멤버를 정의하는 경우, 마지막 멤버를 불완전 배열 타입으로 추가 선언할 수 있습니다. 유연 배열 멤버의 요소에 접근할 때(연산자
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의 정의 }
키워드
참고 사항
구조체 초기화 규칙에 대해서는 구조체 초기화 를 참조하십시오.
불완전한 타입의 멤버는 허용되지 않으며, 구조체 타입은 정의가 끝날 때까지 완전하지 않기 때문에, 구조체는 자신의 타입을 멤버로 가질 수 없습니다. 자신의 타입에 대한 포인터는 허용되며, 일반적으로 연결 리스트나 트리의 노드를 구현하는 데 사용됩니다.
구조체 선언은 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++ 문서
에서
클래스 선언
참조
|