# array literals in c part of [c programming](/computer/guides/c.html) this document describes how initial array contents can be expressed in c. it complements "array representations" by focusing on construction syntax and initialization forms, not storage models or mutation semantics. c does not have array values in the abstract sense. it has objects with storage duration that may be initialized using literal syntax. all mechanisms described below operate within that constraint. ## purpose and limits - array literals in c exist only as part of object initialization - there is no general expression that evaluates to an array value - literal syntax therefore always implies - a concrete object - a defined storage duration - a fixed element type ## aggregate initialization - initializer lists used at object definition - applies to arrays and structs - object has automatic or static storage duration ```c int a[] = {1, 2, 3}; int b[3][2] = { {1, 2}, {3, 4}, {5, 6} }; ``` * initializer appears only at the point of definition * size may be inferred from the initializer * for multidimensional arrays, all but the first dimension must be known * large initializers may be placed in separate files via include ## compound literals * unnamed objects introduced by a type name followed by an initializer * available since c99 * usable as expressions ```c int *a = (int[]){1, 2, 3, 4}; ``` * storage duration * automatic inside a function * static at file scope * commonly used to * initialize pointers * pass literal arrays to functions ```c void print_array(int *a, size_t n); print_array((int[]){1, 2, 3, 4}, 4); ``` * semantically close to aggregate initialization * main distinction is the absence of a declared object name ## literal data and the heap * heap-allocated arrays cannot be initialized directly * literal syntax can only produce temporary or static objects * common pattern * allocate * copy from a literal source ```c int *a = malloc(4 * sizeof(int)); memcpy(a, (int[]){1, 2, 3, 4}, 4 * sizeof(int)); ``` * compound literals are often used as the copy source * allocation failure handling follows normal malloc conventions ## structs as array carriers * arrays embedded in structs participate in aggregate initialization * the struct becomes a value-like container for the array ```c typedef struct { int data[3]; } int_array3_t; int_array3_t x = { .data = {1, 2, 3} }; ``` * the entire struct can be copied, passed, or returned * element access requires a field name ```c x.data[1]; ``` * layout may include padding for alignment * copying semantics are defined at the struct level ### flexible array members * since c99, the last struct member may be an array of unspecified size ```c typedef struct { size_t size; int data[]; } int_array_t; ``` * such structs cannot be initialized with array literals * they must be allocated dynamically * access is always via pointer * they do not have value-like copy semantics ## macros and literal forwarding * macros may expand to initializer lists or compound literals * used to reduce verbosity or simulate literal syntax ```c #define int_array(...) ((int[]){__VA_ARGS__}) int *a = int_array(1, 2, 3, 4); ``` * advantages * concise call-site syntax * no additional runtime code * limitations * type is fixed or implicit * debugging and error reporting are weaker * semantics depend on expansion context ### heap-targeting macros * macros may combine allocation and copying * relies on nonstandard extensions or statement expressions ```c #define new_array(size, ...) ({ \ int *_a = malloc(size * sizeof(int)); \ if (_a) { \ int _t[] = {__VA_ARGS__}; \ memcpy(_a, _t, size * sizeof(int)); \ } \ _a; \ }) ``` * not portable * semantics dominated by allocation policy, not literal syntax ## functions as constructors * functions that allocate and populate arrays * fixed-arity or variadic ```c int *int_array4(int a, int b, int c, int d); ``` ```c int *new_array(size_t n, ...); ``` * these do not introduce new literal forms * they encode initialization procedurally * behavior is governed by calling convention and allocation semantics ## summary * aggregate initialization and compound literals are the only native literal forms * all other techniques reuse these forms indirectly * literal syntax always implies an object and a storage duration * heap arrays require a two-step process: allocation plus initialization * representation and mutation concerns are treated separately in "array representations"