Namespaces
Variants

Compound literals (since C99)

From cppreference.net

지정된 유형(구조체, 공용체 또는 배열 유형일 수도 있음)의 이름 없는 객체를 제자리에서 생성합니다.

목차

구문

( storage-class-specifiers  (선택 사항) (C23부터) type ) { initializer-list } (C99부터)
( storage-class-specifiers  (선택 사항) (C23부터) type ) { initializer-list , } (C99부터)
( storage-class-specifiers  (선택 사항) type ) { } (C23부터)

여기서

storage-class-specifiers - (C23부터) 다음 중 하나만 포함할 수 있는 저장 클래스 지정자 목록: constexpr , static , register , 또는 thread_local
type - 완전한 객체 타입이나 크기가 알려지지 않은 배열을 지정하는 타입 이름 (VLA는 제외)
initializer-list - type 객체의 초기화 에 적합한 초기화자 목록

설명

복합 리터럴 표현식은 type 으로 지정된 타입의 무명 객체를 생성하고 initializer-list 로 지정된 방식으로 초기화합니다. 지정 초기화자(Designated initializers) 가 허용됩니다.

복합 리터럴의 타입은 type 입니다 (단, type 이 크기를 알 수 없는 배열인 경우는 제외; 이 경우 크기는 배열 초기화 에서와 같이 initializer-list 로부터 추론됩니다).

복합 리터럴의 값 범주는 lvalue 입니다(주소를 취할 수 있음).

복합 리터럴이 평가하는 이름 없는 객체는 파일 범위에서 복합 리터럴이 발생하는 경우 정적 저장 기간 을 가지며, 블록 범위에서 복합 리터럴이 발생하는 경우 자동 저장 기간 을 가집니다(이 경우 객체의 수명 은 포함하는 블록의 끝에서 종료됩니다). (C23 이전)
복합 리터럴이 함수 본문 외부 및 모든 매개변수 목록 외부에서 평가되면 파일 범위와 연관됩니다. 그렇지 않으면 포함하는 블록과 연관됩니다. 이 연관에 따라 저장 클래스 지정자(비어 있을 수 있음), 타입 이름 및 초기화 목록(있는 경우)은 각각 다음 형식의 파일 범위 또는 블록 범위에서 객체 정의에 대한 유효한 지정자여야 합니다:
storage-class-specifiers type typeof( type ) ID = { initializer-list } ;
여기서 ID 는 전체 프로그램에 대해 고유한 식별자입니다. 복합 리터럴은 값, 타입, 저장 기간 및 기타 속성이 위의 정의 구문에 의해 주어진 것과 같은 이름 없는 객체를 제공합니다. 저장 기간이 자동인 경우, 이름 없는 객체 인스턴스의 수명은 포함하는 블록의 현재 실행입니다. 저장 클래스 지정자에 constexpr , static , register , 또는 thread_local 이외의 다른 지정자가 포함된 경우 동작은 정의되지 않습니다.
(C23 이후)

참고 사항

const로 한정된 문자 또는 와이드 문자 배열 타입의 복합 리터럴은 문자열 리터럴 과 저장 공간을 공유할 수 있습니다.

(const char []){"abc"} == "abc" // 1 또는 0일 수 있으며, 지정되지 않음

각 복합 리터럴은 해당 스코프에서 단일 객체만 생성합니다:

#include <assert.h>
int main(void)
{
    struct S
    {
        int i;
    }
    *p = 0, *q;
    int j = 0;
again:
    q = p,
    p = &((struct S){ j++ }); // S 타입의 이름 없는 객체를 생성하고,
                              // 이전에 j가 보유했던 값으로 초기화한 다음,
                              // 이 이름 없는 객체의 주소를 포인터 p에 할당합니다
    if (j < 2)
        goto again; // 참고: 루프를 사용했다면 여기서 범위가 종료되어,
                    // 복합 리터럴의 수명이 끝나 p가 댕글링 포인터가 됩니다
    assert(p == q && q->i == 1);
}

복합 리터럴은 이름이 없기 때문에, 복합 리터럴은 자기 자신을 참조할 수 없습니다 (이름이 있는 구조체는 자기 자신을 가리키는 포인터를 포함할 수 있습니다).

복합 리터럴의 구문은 캐스트 와 유사하지만, 중요한 차이점은 캐스트는 비 lvalue 표현식인 반면 복합 리터럴은 lvalue라는 점입니다.

예제

#include <stdio.h>
int *p = (int[]){2, 4}; // 이름 없는 정적 int[2] 배열을 생성하고
                        // 배열을 {2, 4} 값으로 초기화하며
                        // 배열의 첫 번째 요소를 가리키는 포인터 p를 생성
const float *pc = (const float []){1e0, 1e1, 1e2}; // 읽기 전용 복합 리터럴
struct point {double x,y;};
int main(void)
{
    int n = 2, *p = &n;
    p = (int [2]){*p}; // 이름 없는 자동 int[2] 배열을 생성하고
                       // 첫 번째 요소를 이전에 *p가 가리키던 값으로 초기화하며
                       // 두 번째 요소를 0으로 초기화하고
                       // 첫 번째 요소의 주소를 p에 저장
    void drawline1(struct point from, struct point to);
    void drawline2(struct point *from, struct point *to);
    drawline1(
        (struct point){.x=1, .y=1},  // 블록 범위를 가진 두 개의 구조체를 생성하고
        (struct point){.x=3, .y=4}); // drawline1을 호출하여 값으로 전달
    drawline2(
        &(struct point){.x=1, .y=1},  // 블록 범위를 가진 두 개의 구조체를 생성하고
        &(struct point){.x=3, .y=4}); // drawline2를 호출하여 주소로 전달
}
void drawline1(struct point from, struct point to)
{
    printf("drawline1: `from` @ %p {%.2f, %.2f}, `to` @ %p {%.2f, %.2f}\n",
        (void*)&from, from.x, from.y, (void*)&to, to.x, to.y);
}
void drawline2(struct point *from, struct point *to)
{
    printf("drawline2: `from` @ %p {%.2f, %.2f}, `to` @ %p {%.2f, %.2f}\n",
        (void*)from, from->x, from->y, (void*)to, to->x, to->y);
}

가능한 출력:

drawline1: `from` @ 0x7ffd24facea0 {1.00, 1.00}, `to` @ 0x7ffd24face90 {3.00, 4.00}
drawline2: `from` @ 0x7ffd24facec0 {1.00, 1.00}, `to` @ 0x7ffd24faced0 {3.00, 4.00}

참고문헌

  • C23 표준 (ISO/IEC 9899:2024):
  • 6.5.2.5 복합 리터럴 (p: 77-80)
  • C17 표준 (ISO/IEC 9899:2018):
  • 6.5.2.5 복합 리터럴 (p: 61-63)
  • C11 표준 (ISO/IEC 9899:2011):
  • 6.5.2.5 복합 리터럴 (p: 85-87)
  • C99 표준 (ISO/IEC 9899:1999):
  • 6.5.2.5 복합 리터럴 (p: 75-77)