Fundamentals 29 min read

Mastering C Structs: Definitions, Alignment, and Practical Usage

This guide explains C structs—from basic definitions and declaration styles to memory alignment, byte sizing, bit‑field handling, nested structures, and using structs as function parameters—providing clear code examples and practical tips for efficient embedded and systems programming.

Liangxu Linux
Liangxu Linux
Liangxu Linux
Mastering C Structs: Definitions, Alignment, and Practical Usage

What Is a Struct?

A struct groups heterogeneous data members into a single composite type.

Declaration vs Definition

A declaration describes the layout without allocating memory:

struct book {
    char title[MAXTITL];
    char author[MAXAUTL];
    float value;
};

Variables are created after the type is declared.

Three Common Ways to Define a Struct in C

Separate declaration and definition :

#include <stdio.h>
struct student {
    int   age;
    float score;
    char  sex;
};
int main(void) {
    struct student a = {20, 79.0f, 'f'};
    printf("Age:%d Score:%.2f Sex:%c
", a.age, a.score, a.sex);
    return 0;
}

Combined declaration‑definition (single use):

#include <stdio.h>
struct student { int age; float score; char sex; } a = {21, 80, 'n'};
int main(void) {
    printf("Age:%d Score:%.2f Sex:%c
", a.age, a.score, a.sex);
    return 0;
}

Anonymous struct variable (no tag name) :

#include <stdio.h>
struct { int age; float score; char sex; } t = {21, 79, 'f'};
int main(void) {
    printf("Age:%d Score:%f Sex:%c
", t.age, t.score, t.sex);
    return 0;
}

Defining Struct Variables

After the type is declared, create variables:

struct book library;   // allocates one instance

Access members with the dot operator:

printf("%s
%s
%f", library.title, library.author, library.value);

Initialization

Struct initialization mirrors array initialization using braces and commas. Each initializer must match the corresponding member’s type.

struct book s1 = {
    "yuwen",          // title
    "guojiajiaoyun", // author
    22.5               // value
};

For objects with static storage, all initializer values must be constant expressions.

Memory Alignment and Size

Compilers align each member to its natural boundary (often 4 bytes on 32‑bit CPUs). Padding may be added after members and at the end of the struct. Alignment can be changed with #pragma pack(n) where n is 1, 2, 4, 8, or 16.

Alignment Rules

Each member is placed at the smallest offset that satisfies both the #pragma pack value and the member’s own size.

After all members are placed, the whole struct is padded to satisfy the same rule using the largest member size.

If the #pragma pack value is greater than or equal to all member sizes, it has no effect.

Example (8‑byte struct due to a 4‑byte int member):

typedef struct {
    char addr;   // 1 byte
    char name;   // 1 byte
    int  id;     // 4 bytes, forces 4‑byte alignment
} PERSON;

Bit‑Field Structs

Bit fields pack sub‑byte data within an underlying integer type.

typedef struct {
    uint32_t SYMBOL_TYPE   :5;
    uint32_t reserved_1    :4;
    uint32_t SYMBOL_NUMBER :7;
    uint32_t SYMBOL_ACTIVE :1;
    uint32_t SYMBOL_INDEX  :8;
    uint32_t reserved_2    :8;
} SYMBOL_STRUCT;

The compiler allocates space in units of the underlying type (here 4 bytes). When a bit‑field does not fit in the current unit, a new unit is started.

Nested Structs

Structs can contain other structs, enabling hierarchical data models.

typedef struct {
    char addr;
    char name;
    int  id;
} PERSON;

typedef struct {
    char   age;
    PERSON ps1;   // nested struct
} STUDENT;

Access nested members with chained dot operators, e.g., student.ps1.id .

Structs as Function Parameters

Encapsulating related arguments in a struct reduces repetitive code and improves maintainability. Pass by pointer to avoid copying the whole struct.

struct video_info {
    char *name;
    long  address;
    int   size;
    int   alg;
    time_t time;
};

int get_video(struct video_info *v);
int handle_video(struct video_info *v);
int send_video(struct video_info *v);

int main(void) {
    struct video_info v = {0};
    get_video(&v);
    handle_video(&v);
    send_video(&v);
    return 0;
}

Passing a pointer avoids the overhead of copying; passing by value is also possible but incurs extra cost.

Struct Size, Padding, and Ordering

The size of a struct is not simply the sum of its members because of alignment padding. Reordering members can reduce wasted space.

typedef struct {
    char  addr;   // 1 byte
    char  name;   // 1 byte
    int   id;     // 4 bytes (adds 2 bytes padding after the two chars)
} PERSON; // sizeof(PERSON) == 8 on a 32‑bit system

Placing the largest member first often yields a smaller overall size:

typedef struct {
    int   id;    // 4 bytes
    char  addr;  // 1 byte
    char  name;  // 1 byte
} PERSON; // sizeof(PERSON) == 6 (plus possible trailing padding to satisfy overall alignment)

Typedef for Convenience

Use typedef to create an alias for a struct type, making declarations shorter.

typedef struct {
    int   date;
    /* … */
} STUDENT;

STUDENT s1, s2;   // no need to write "struct" each time

Bit‑Field Layout Details (Embedded Example)

When the underlying type is uint32_t , the compiler allocates 4‑byte units. Changing the base type to uint8_t reduces the allocation unit to 1 byte.

typedef struct {
    uint8_t SYMBOL_TYPE   :5;
    uint8_t reserved_1    :4;
    uint8_t SYMBOL_NUMBER :7;
    uint8_t SYMBOL_ACTIVE :1;
    uint8_t SYMBOL_INDEX  :8;
    uint8_t reserved_2    :8;
} SYMBOL_STRUCT;

In this configuration the entire bit‑field occupies exactly 4 bytes (32 bits) with no extra padding.

Key Takeaways

Structs provide a way to group heterogeneous data into a single type.

Memory alignment and padding affect the actual size; #pragma pack can control alignment.

Bit fields pack data tightly within an underlying integer type, but alignment rules still apply.

Nested structs enable complex data hierarchies.

Encapsulating related parameters in a struct reduces code duplication and improves maintainability.

Member ordering and appropriate use of typedef can minimize padding and struct size.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Cmemory alignmentembeddedInitializationstructbit-fields
Liangxu Linux
Written by

Liangxu Linux

Liangxu, a self‑taught IT professional now working as a Linux development engineer at a Fortune 500 multinational, shares extensive Linux knowledge—fundamentals, applications, tools, plus Git, databases, Raspberry Pi, etc. (Reply “Linux” to receive essential resources.)

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.