Declaring and Initializing Structures in C

C structures in the C language are a capability used to combine data of different types together, allowing us to create custom data types with multiple fields within our code. Mastering the correct declaration and initialization of structures, as well as understanding their storage mechanism in memory, is key to writing efficient and maintainable C programs. This article will delve into how to define and use structures according to standards and analyze their underlying principles to optimize performance.
Before we begin, it is crucial to clarify a core concept: the C language itself does not mandate specific keywords for declaring variable types or initializing data (such as new in Java). However, to maintain code consistency and readability, the industry generally adheres to the following best practices:

  • Explicit Definition: For structure members, it is recommended to use lowercase letters for naming to distinguish them from global variables. For example, name fields as name, age, or score rather than all-uppercase names like NAME or AGE. This helps avoid confusion with macro definitions or function names.
  • Type Matching: The type of each field should reflect its actual purpose as precisely as possible; do not use overly broad types (such as using int) to save effort when the data might be a floating-point number.

Structure Declaration Syntax and Multi-dimensional Extension

When defining a structure, we simply need to list all fields along with their corresponding data types. The C language allows us to accommodate different kinds of data objects within the same block of memory space; this is one manifestation of its flexibility. Furthermore, when handling nested data structures (i.e., "structures in arrays" or "structures inside structures"), the declaration method remains consistent, but attention must be paid to reference relationships:

// Define a simple structure type containing two integers
typedef struct {
    int id;      // Member variables should use lowercase letters for naming
    float score; 
} StudentInfo;

// Create an instance of this type (equivalent to initialization)
StudentInfo s1 = { .id = 101, .score = 95.5 };

The code above demonstrates how the typedef keyword is used to create an alias for a structure, thereby simplifying subsequent usage processes. It is worth noting that under C89/C90 standards, we typically need to use curly braces {} immediately after definition for initialization; whereas under newer standards (C99 and later), although comma-separated list forms like StudentInfo s1 = {id: 101, score: 95.5} are supported, the dot notation is recommended for compatibility and clarity reasons.

Memory Layout and Alignment Mechanism Analysis

Understanding how structures are actually stored in computer memory is crucial for performance optimization. When a structure is assigned to a variable, the compiler calculates the total number of bytes required based on its field types. There exists a key detail here that is often overlooked: offsets between members and padding.

Assume we have a structure defined as follows:

struct Data {
    char a; // 1 byte, offset 0
    int b;  // 4 bytes, offset 4 (because ints are typically aligned to 4-byte boundaries)
};

In this example, char a occupies address 0x00. The subsequent int b, while logically following immediately after, has three blank spaces filled between it and a because CPUs require high efficiency for reading integer data (accessing by word length). Consequently, the address of b starts at 0x04. Therefore, the actual size of this structure is $1 + 3 (\text{padding}) + 4 = 8$ bytes, rather than simply $1+4=5$ bytes.

This memory waste phenomenon is known as "alignment overhead." If we mix fields of different sizes within a structure (such as char, int, and long long), it may lead to significant extra space occupation. In high-performance computing or embedded development scenarios, developers often need to manually control the memory layout by adjusting member order or using features like _Alignas (C11) to reduce waste and improve cache hit rates.

Initialization Strategies and Precautions

Initializing a structure is the first step in constructing complex data objects. Depending on different versions of C language and compiler implementations, there are multiple initialization methods:

  • List Form (Recommended): StudentInfo s1 = { .id = 101, .score = 95.5 }; — This method is clear and less prone to errors.
  • Comma-separated Form: StudentInfo s2; s2.id = 101; s2.score = 95.5; — Applicable for C89, but somewhat cumbersome in modern code.

It should be noted that if a structure contains pointer members (such as char *name), direct assignment cannot allocate memory space for them; instead, they should be initialized to NULL or the address of dynamically allocated memory:

struct Address {
    char city[20]; // Array name is equivalent to the address of its first element
    int zip;       // Integer variable
};

// Incorrect demonstration: Directly assigning a pointer (without allocating memory)
struct Address addr = {"Beijing", "100000"}; 

// Correct approach: Initialize first, then dynamically allocate or pass an existing string
char *city_name = "Shanghai"; 
int zip_code = 200000;

struct Address addr_init = { .city = city_name, .zip = zip_code };

Summary and Best Practice Recommendations

Structures are the core tool for building complex data models in C. Through reasonable declaration, standardized naming conventions, and a deep understanding of memory alignment mechanisms, we can write code that is both efficient and easy to understand. In actual development, please pay attention to the following points:

  1. Maintain Field Order: Place larger type members first (or sort them according to natural logic), which usually reduces padding space and improves access efficiency.
  2. Avoid Uninitialized Pointers: Always ensure that pointer variables within a structure are explicitly initialized to NULL or complete memory allocation before assignment, preventing runtime errors caused by dangling pointers.
  3. Utilize typedef: For frequently used complex structure types, defining aliases using typedef can significantly improve code readability and reusability.

After mastering these techniques, you will be able to handle C language structures more freely, laying a solid foundation for learning advanced data structures such as linked lists and trees in the future.