Question

How to Initialize a Struct in C: Declaration, Assignment, and Standard-Compliant Syntax

cstructinitialization

Question

I want to initialize a struct variable, but I want the declaration and the value setup to happen separately. Here is the code I have:

typedef struct MY_TYPE {
    bool flag;
    short int value;
    double stuff;
} MY_TYPE;

void function(void) {
    MY_TYPE a;
    /* ... */
    a = { true, 15, 0.123 };
}

Is this a valid and standard-compliant way to declare and initialize a local variable of type MY_TYPE in C standards such as C89, C90, C99, and C11?

If not, what is the correct or preferred way to do this?

A later update mentioned using a static initializer object where each member is set explicitly.

Short Answer

By the end of this page, you will understand the difference between struct initialization and struct assignment in C, why a = { ... }; is not valid standard C syntax, and how to correctly create and populate structs using declaration-time initializers, compound literals, member-by-member assignment, or a prebuilt template value.

Concept

In C, initialization and assignment are related but different operations.

  • Initialization happens when a variable is declared.
  • Assignment happens after the variable already exists.

That distinction matters for structs.

Initialization at declaration

When you declare a struct variable, you can initialize it with braces:

MY_TYPE a = { true, 15, 0.123 };

This is called an initializer. It is valid C syntax.

Assignment after declaration

Once the variable has already been declared, you cannot use a bare brace initializer by itself:

MY_TYPE a;
a = { true, 15, 0.123 };   /* invalid */

That is not valid standard C.

Why?

Because { ... } by itself is not a normal expression in C. Initializer lists are used in declarations, not as standalone assignment values.

Correct ways to assign a struct later

You have a few standard-compliant options:

  1. Assign members one by one
MY_TYPE a;
a.flag = true;
a.value = 15;
a.stuff = 0.123;
  1. Assign from another struct object
MY_TYPE temp = { true, 15, 0.123 };
MY_TYPE a;
a = temp;
  1. Use a compound literal (C99 and later)
MY_TYPE a;
a = (MY_TYPE){ true, 15, 0.123 };

This creates a temporary struct value and assigns it to a.

Why this matters in real programming

Structs are used everywhere in C:

  • configuration objects
  • return values from functions
  • data records
  • state tracking
  • hardware register representations

Knowing when to use an initializer versus an assignment helps you write code that is both correct and portable across C standards.

Mental Model

Think of a struct like a filled-out paper form with several fields.

  • Initialization is filling in the form at the moment you create it.
  • Assignment is replacing the entire form later with another completed form.
  • Member assignment is erasing and rewriting one field at a time.

The syntax { true, 15, 0.123 } is like a blank template used when creating the form. In older C rules, you cannot just hand that template directly to an already existing variable unless you wrap it as a real struct value using a compound literal in C99+.

Take Quiz

Syntax and Examples

1. Initialize when declaring the variable

MY_TYPE a = { true, 15, 0.123 };

This is valid C initialization syntax.

2. Assign each member separately

MY_TYPE a;
a.flag = true;
a.value = 15;
a.stuff = 0.123;

This works in all common C standards.

3. Assign from another struct variable

MY_TYPE a;
MY_TYPE b = { true, 15, 0.123 };
a = b;

Struct assignment is allowed in C. The whole struct is copied.

4. Use a compound literal (C99 and later)

MY_TYPE a;
a = (MY_TYPE){ true, 15, 0.123 };

This is often the cleanest way to assign all fields at once after declaration.

5. Use designated initializers (C99 and later)

MY_TYPE a = { .flag = true, .value = 15, .stuff = 0.123 };

This is especially useful when:

  • you want clarity
  • you do not want to rely on field order
  • you only want to set some members

Example:

MY_TYPE a = { .value = 15 };

Any omitted members are initialized to zero.

6. Static template object

static const MY_TYPE default_value = { true, 15, 0.123 };

void function(void) {
    MY_TYPE a;
    a = default_value;
}

This is useful when the same starting value is reused in many places.

Step by Step Execution

Consider this C99+ example:

#include <stdbool.h>

typedef struct {
    bool flag;
    short int value;
    double stuff;
} MY_TYPE;

void function(void) {
    MY_TYPE a;
    a = (MY_TYPE){ true, 15, 0.123 };
}

Step by step

  1. MY_TYPE is defined as a struct type with three members.
  2. Inside function, a is declared.
  3. At this point, a exists, but its members are uninitialized because it is a local automatic variable.
  4. The expression (MY_TYPE){ true, 15, 0.123 } creates a temporary struct value of type MY_TYPE.
  5. That temporary value is assigned to a using normal struct assignment.
  6. After the assignment:
    • a.flag is true
    • a.value is 15
    • a.stuff is 0.123

Equivalent member-by-member version

void function(void) {
    MY_TYPE a;
    a.flag = true;
    a.value = 15;
    a.stuff = 0.123;
}

Both produce a fully assigned struct value, but the compound literal version is shorter.

Important note about C89/C90

This line is not available in C89/C90:

a = (MY_TYPE){ true, 15, 0.123 };

For those standards, use member assignment or assign from another already declared struct object.

Real World Use Cases

Struct initialization and assignment show up in many practical C programs.

Configuration objects

typedef struct {
    int port;
    int debug;
} Config;

Config cfg = { 8080, 1 };

Used for program settings loaded at startup.

Returning multiple values from a function

typedef struct {
    int code;
    double result;
} Response;

Response make_response(void) {
    return (Response){ 200, 42.5 };
}

Useful when a function needs to return related values together.

Resetting state

typedef struct {
    int count;
    int error;
} State;

void reset_state(State *s) {
    *s = (State){ 0, 0 };
}

This is common in state machines and embedded code.

Default objects

static const MY_TYPE defaults = { false, 0, 0.0 };

Reusable defaults are common in libraries and device drivers.

Take Quiz

Real Codebase Usage

In real projects, developers usually choose one of a few clear patterns.

Pattern 1: Initialize immediately when possible

MY_TYPE item = { true, 15, 0.123 };

This is the simplest and safest option.

Pattern 2: Use designated initializers for readability

MY_TYPE item = {
    .flag = true,
    .value = 15,
    .stuff = 0.123
};

This is common when structs have many fields.

Pattern 3: Use a default template and then customize

static const MY_TYPE default_item = { false, 0, 0.0 };

MY_TYPE item = default_item;
item.value = 15;

This is common in configuration-heavy code.

Pattern 4: Reset a struct with one assignment

*obj = (MY_TYPE){ 0 };

This sets all members to zero in C99+.

Pattern 5: Validate after assignment

MY_TYPE item = { true, 15, 0.123 };
if (item.value < 0) {
    /* handle invalid data */
}

Struct setup is often followed by validation or guard clauses.

Pattern 6: Factory-style helper function

MY_TYPE make_my_type(bool flag, short int value, double stuff) {
    MY_TYPE x = { flag, value, stuff };
    return x;
}

This keeps initialization rules in one place.

Common Mistakes

Mistake 1: Using a brace initializer in assignment

MY_TYPE a;
a = { true, 15, 0.123 };   /* invalid */

Why it is wrong

A bare initializer list is not a valid assignment expression in standard C.

Fix

a = (MY_TYPE){ true, 15, 0.123 };   /* C99+ */

or

a.flag = true;
a.value = 15;
a.stuff = 0.123;

Mistake 2: Forgetting that local structs are uninitialized

MY_TYPE a;
/* using a.value here is unsafe */

Fix

Always initialize before use.

MY_TYPE a = { 0 };

Mistake 3: Using bool in C89/C90 without support

bool flag;

Why this can fail

bool is standardized in C99 through stdbool.h.

Fix

For C99+:

#include <stdbool.h>

For older C code, some projects use int or custom typedefs.

Mistake 4: Relying on field order too casually

MY_TYPE a = { 15, true, 0.123 };   /* wrong order */

Fix

Keep the correct member order, or use designated initializers.

MY_TYPE a = { .flag = true, .value = 15, .stuff = 0.123 };

Mistake 5: Assuming all syntax works in all C standards

Compound literals and designated initializers are C99+ features.

Fix

If portability to C89/C90 matters, prefer:

  • declaration-time positional initialization
  • member-by-member assignment
  • assignment from another named struct variable

Comparisons

ApproachExampleWorks in C89/C90Works in C99/C11Notes
Declaration-time initializationMY_TYPE a = { true, 15, 0.123 };Partly, but bool needs careYesStandard initializer at declaration
Bare brace assignmenta = { true, 15, 0.123 };NoNoInvalid standard C
Member-by-member assignmenta.flag = true;YesYesMost portable
Assign from another struct variablea = b;YesYesWhole-struct copy
Compound literal assignmenta = (MY_TYPE){ true, 15, 0.123 };NoYesClean and concise
Designated initializerMY_TYPE a = { .value = 15 };NoYesClearer for large structs

Initialization vs assignment

ConceptWhen it happensExample
InitializationAt declarationMY_TYPE a = { true, 15, 0.123 };
AssignmentAfter declarationa = other_struct;

That distinction is the key idea behind the original question.

Take Quiz

Cheat Sheet

/* Struct type */
typedef struct {
    bool flag;
    short int value;
    double stuff;
} MY_TYPE;
/* Valid: initialize at declaration */
MY_TYPE a = { true, 15, 0.123 };
/* Invalid: bare brace assignment */
MY_TYPE a;
a = { true, 15, 0.123 };   /* not standard C */
/* Valid: assign members individually */
a.flag = true;
a.value = 15;
a.stuff = 0.123;
/* Valid: assign from another struct */
MY_TYPE b = { true, 15, 0.123 };
a = b;
/* Valid in C99+: compound literal */
a = (MY_TYPE){ true, 15, 0.123 };
/* Valid in C99+: designated initializer */
MY_TYPE a = { .flag = true, .value = 15, .stuff = 0.123 };
/* Zero-initialize all members */
MY_TYPE a = { 0 };

Rules to remember

  • Use { ... } directly only during declaration.
  • Use (Type){ ... } for assignment in C99+.
  • Struct assignment copies all members.
  • bool requires #include <stdbool.h> in C99+.
  • For C89/C90 portability, avoid compound literals and designated initializers.

FAQ

Is a = {1, 2, 3}; valid in C?

No. A bare brace initializer is not valid as a normal assignment expression in standard C.

How do I assign a struct after declaration in C?

Use member-by-member assignment, assign from another struct variable, or use a compound literal in C99 and later.

What is the difference between initializing and assigning a struct?

Initialization happens when the variable is declared. Assignment happens later, after the variable already exists.

Can I copy one struct to another with = in C?

Yes. If both variables have the same struct type, = performs whole-struct assignment.

Are compound literals available in C89 or C90?

No. Compound literals were added in C99.

Can I partially initialize a struct in C?

Yes. At declaration time, omitted members are zero-initialized. In C99+, designated initializers make this clearer.

Is bool valid in all C standards?

No. bool is standard in C99 and later through stdbool.h. Older C code often uses int instead.

Take Quiz

Mini Project

Description

Create a small C program that manages a SensorConfig struct. The goal is to practice the difference between declaration-time initialization and post-declaration assignment. You will create a default configuration, copy it into another variable, and then customize individual fields.

Goal

Build a program that creates struct values correctly using standard C syntax and prints the final configuration.

Requirements

  • Define a struct with at least three fields.
  • Create one default struct value using declaration-time initialization.
  • Create another struct variable and assign it from the default value.
  • Update at least one field after assignment.
  • Print the final values to verify the result.
Take Quiz

Keep learning

Related questions

View more