Namespaces
Variants

Compound literals (since C99)

From cppreference.net

指定された型(構造体、共用体、あるいは配列型でさえも可能)の無名オブジェクトをその場で構築します。

目次

構文

( ストレージクラス指定子  (オプション) (C23以降) ) { 初期化子リスト } (C99以降)
( ストレージクラス指定子  (オプション) (C23以降) ) { 初期化子リスト , } (C99以降)
( ストレージクラス指定子  (オプション) ) { } (C23以降)

ここで

storage-class-specifiers - (C23以降) 記憶クラス指定子 のリストで、 constexpr , static , register , または thread_local のみを含むことができる
type - 任意の完全オブジェクト型または未知サイズの配列を指定する 型名 (VLAを除く)
initializer-list - type のオブジェクトの 初期化 に適した初期化子のリスト

説明

複合リテラル式は、 type で指定された型の無名オブジェクトを構築し、 initializer-list で指定された方法で初期化します。 指示付き初期化子 が使用可能です。

複合リテラルの型は type である(ただし type が未知サイズの配列である場合は除く。この場合、サイズは array initialization と同様に initializer-list から推論される)。

複合リテラルの値カテゴリは lvalue です(そのアドレスを取得できます)。

複合リテラルが評価される無名オブジェクトは、 複合リテラルがファイルスコープで現れた場合は静的 記憶域期間 を持ち、複合リテラルがブロックスコープで現れた場合は自動 記憶域期間 を持つ(この場合、オブジェクトの 寿命 は囲んでいるブロックの終わりで終了する)。 (C23まで)
複合リテラルが関数本体の外側かつ任意のパラメータリストの外側で評価される場合、それはファイルスコープに関連付けられる。それ以外の場合、それは囲んでいるブロックに関連付けられる。この関連付けに応じて、記憶クラス指定子(空の場合もある)、型名、および初期化子リスト(存在する場合)は、以下の形式のファイルスコープまたはブロックスコープでのオブジェクト定義に対する有効な指定子でなければならない:
記憶クラス指定子 typeof( ) 識別子 = { 初期化子リスト } ;
ここで 識別子 はプログラム全体で一意な識別子である。複合リテラルは、値、型、記憶域期間、その他のプロパティが上記の定義構文で与えられたかのような無名オブジェクトを提供する。記憶域期間が自動の場合、無名オブジェクトのインスタンスの寿命は囲んでいるブロックの現在の実行である。記憶クラス指定子が 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);
}

複合リテラルは名前を持たないため、複合リテラルは自身を参照できません(名前付き構造体は自身へのポインタを含むことができます)。

複合リテラルの構文は キャスト と似ていますが、重要な違いは、キャストが非左辺値式であるのに対し、複合リテラルは左辺値である点です。

#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が保持していた値で初期化
                       // 2番目の要素をゼロで初期化
                       // 最初の要素のアドレスをpに格納
    void drawline1(struct point from, struct point to);
    void drawline2(struct point *from, struct point *to);
    drawline1(
        (struct point){.x=1, .y=1},  // ブロックスコープで2つの構造体を作成し
        (struct point){.x=3, .y=4}); // drawline1を呼び出し、値渡しで渡す
    drawline2(
        &(struct point){.x=1, .y=1},  // ブロックスコープで2つの構造体を作成し
        &(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)