Compound Literals and Designated Initializers
This chapter covers two C99 features that make initialization and temporary values much more convenient: compound literals and designated initializers.
Why This Matters
Designated initializers make code self-documenting and robust against member reordering. Compound literals let you create temporary struct or array values inline, avoiding separate declarations. Both are widely used in modern C.
Prerequisites
c.core.21— arrays and decay.c.core.26— structs.
Core Concept
Designated initializers (C99)
A designated initializer names the member or element being initialized, rather than relying on position:
struct Point p = { .x = 1, .y = 2 };
int arr[5] = { [2] = 7, [4] = 9 };
Unmentioned members/elements are initialized to zero (for objects with static or aggregate initialization).
Compound literals (C99)
A compound literal creates an unnamed object of a given type:
(struct Point){ .x = 1, .y = 2 }
(int[]){ 1, 2, 3 }
It can be used anywhere an expression of that type is valid. At block scope it has automatic lifetime; at file scope it has static lifetime.
Syntax
(type-name){ initializer-list }
.member = value
[index] = value
Examples
Designated initializers
#include <stdio.h>
struct Config {
int retries;
int timeout;
int verbose;
};
int main(void)
{
struct Config c = { .timeout = 30, .verbose = 1 };
printf("%d %d %d\n", c.retries, c.timeout, c.verbose);
return 0;
}
Expected output: 0 30 1 (retries defaults to 0).
Compound literal passed to a function
#include <stdio.h>
struct Point { int x; int y; };
void print_point(struct Point p)
{
printf("(%d, %d)\n", p.x, p.y);
}
int main(void)
{
print_point((struct Point){ .x = 3, .y = 4 });
return 0;
}
Expected output: (3, 4).
Compound literal array
#include <stdio.h>
int sum(const int *a, int n)
{
int s = 0;
for (int i = 0; i < n; i++) s += a[i];
return s;
}
int main(void)
{
printf("%d\n", sum((int[]){1, 2, 3, 4}, 4));
return 0;
}
Expected output: 10.
How It Works
A designated initializer is translated by the compiler into positional initialization (with the named targets filled in and the rest zeroed). A compound literal is a real object — it has an address, a type, and a lifetime — even though it has no name.
Variations
Nested designated initializers
struct Outer {
int a;
struct Inner { int b; int c; } in;
};
struct Outer o = { .in = { .c = 5 } }; /* nested */
Compound literal at file scope
static const int *defaults = (int[]){ 1, 2, 3 }; /* static lifetime */
Common Mistakes
- Confusing a compound literal with a cast:
(int){5}is a compound literal,
(int)5 is a cast.
- Forgetting that a block-scope compound literal has automatic lifetime
(returning a pointer to it is a dangling-pointer bug).
- Assuming designated initializers are C89 (they are C99+).
Undefined Behavior
- Returning a pointer to a block-scope compound literal and dereferencing it
after the block ends (dangling pointer). VERIFIED
- Using a compound literal of a type that is incompatible with the expected
type (constraint violation).
Portability
- Designated initializers and compound literals are C99 and later; they are not
in C89.
- The lifetime rules (automatic at block scope, static at file scope) are
standard.
Under the Hood
A block-scope compound literal is typically placed on the stack, like a local variable. A file-scope compound literal goes in static storage. The compiler may optimize a compound literal away if its address is never taken and its value can be computed directly.
Practical Usage
- Use designated initializers for config structs and complex aggregate types.
- Use compound literals to pass temporary struct/array values without a
separate variable.
- Be mindful of lifetime when taking the address of a compound literal.
Exercises
1. Initialize a struct with designated initializers, leaving some members defaulted, and print them. 2. Pass a compound literal struct to a function. 3. Demonstrate the lifetime of a block-scope compound literal by returning its address (and explaining the bug). 4. Use a compound literal array with a function.
Deep Challenge
Explain the difference in lifetime and addressability between these two uses, and which is safe:
struct Point *p1(void) { return &(struct Point){1, 2}; } /* ? */
struct Point *p2(void)
{
static struct Point p = {1, 2};
return &p;
}
Then write a correct version of the first using static or allocated storage.
Related Concepts
c.struct.decl— structs.c.arr.decl— arrays.c.lang.lifetime— lifetime rules.
References
- ISO/IEC 9899:2018 §6.5.2.5 (compound literals), §6.7.9 (initialization).
Verification
- Designated initializers and compound literals are C99+.
VERIFIED - Block-scope compound literals have automatic lifetime.
VERIFIED - Unmentioned members are zero-initialized.
VERIFIED - No example was executed during generation unless noted. `Execution not
verified.`
Progress
- [ ] Read
- [ ] Understand
- [ ] Complete examples
- [ ] Complete exercises
- [ ] Complete deep challenge
Concept checkboxes
- [ ] Designated initializers
- [ ] Compound literals
- [ ] Compound literal lifetime
Mastery levels
| Concept | Current level (0–8) | Target level |
|---|---|---|
| c.struct.compound-literal | 0 | 5 |
| c.struct.designated-init | 0 | 5 |