一、结构体(struct)

1. 基本定义与初始化

#include <stdio.h>

// 定义结构体
struct Point {
    int x;
    int y;
};

int main() {
    // 初始化方式1:声明时初始化
    struct Point p1 = {10, 20};

    // 初始化方式2:逐个赋值
    struct Point p2;
    p2.x = 30;
    p2.y = 40;

    printf("p1: (%d, %d)\n", p1.x, p1.y);
    printf("p2: (%d, %d)\n", p2.x, p2.y);

    return 0;
}

2. 结构体嵌套

struct Address {
    char city[50];
    int zip;
};

struct Person {
    char name[50];
    int age;
    struct Address addr;  // 嵌套结构体
};

int main() {
    struct Person p = {"Alice", 25, {"Beijing", 100000}};
    printf("%s lives in %s, zip: %d\n", p.name, p.addr.city, p.addr.zip);
    return 0;
}

3. 结构体指针与 -> 操作符

void printPoint(struct Point *p) {
    printf("Point: (%d, %d)\n", p->x, p->y);
}

int main() {
    struct Point p = {5, 6};
    printPoint(&p);  // 传地址
    return 0;
}

4. typedef 简化结构体声明

typedef struct {
    double real;
    double imag;
} Complex;

int main() {
    Complex c1 = {3.0, 4.0};
    printf("Complex number: %.1f + %.1fi\n", c1.real, c1.imag);
    return 0;
}

5. 结构体数组

#define N 3
struct Student {
    char name[20];
    int score;
};

int main() {
    struct Student students[N] = {
        {"Tom", 88},
        {"Jerry", 92},
        {"Spike", 75}
    };

    for (int i = 0; i < N; i++) {
        printf("%s: %d\n", students[i].name, students[i].score);
    }
    return 0;
}

6. 结构体作为函数参数(值传递 vs 指针传递)

  • 值传递:复制整个结构体(开销大)
  • 指针传递:高效,推荐
// 值传递(不推荐大数据结构)
void updateByValue(struct Point p) {
    p.x += 10;  // 修改的是副本
}

// 指针传递(推荐)
void updateByPointer(struct Point *p) {
    p->x += 10;
}

7. 结构体对齐与 #pragma pack

默认情况下,编译器会对结构体成员进行内存对齐以提升访问效率。可通过 #pragma pack 控制:

#pragma pack(1)  // 取消对齐,按1字节对齐
struct PackedStruct {
    char a;     // 1 byte
    int b;      // 4 bytes
    short c;    // 2 bytes
};  // 总大小:7 bytes(而非默认的12)

#pragma pack()  // 恢复默认对齐

使用场景:网络协议、文件格式解析等需要精确控制内存布局的情况。


二、联合体(union)

联合体的所有成员共享同一块内存,大小等于最大成员的大小。

1. 基本用法

union Data {
    int i;
    float f;
    char str[20];
};

int main() {
    union Data data;
    data.i = 10;
    printf("data.i = %d\n", data.i);

    data.f = 220.5;
    printf("data.f = %.1f\n", data.f);
    // 注意:此时 data.i 的值已不可靠!

    strcpy(data.str, "Hello");
    printf("data.str = %s\n", data.str);
    return 0;
}

⚠️ 联合体一次只能安全使用一个成员!


2. 联合体 + 枚举实现“类型安全”的变体(类似 tagged union)

enum TypeTag { INT, FLOAT, STRING };

struct Variant {
    enum TypeTag type;
    union {
        int i;
        float f;
        char* s;
    } data;
};

void printVariant(struct Variant v) {
    switch (v.type) {
        case INT:    printf("Int: %d\n", v.data.i); break;
        case FLOAT:  printf("Float: %.2f\n", v.data.f); break;
        case STRING: printf("String: %s\n", v.data.s); break;
    }
}

int main() {
    struct Variant v1 = {INT, .data.i = 42};
    struct Variant v2 = {STRING, .data.s = "Union example"};
    printVariant(v1);
    printVariant(v2);
    return 0;
}

这是 C 语言中模拟“多态”或“可变类型”的常用技巧。


3. 联合体用于字节级操作(如大小端检测)

int isLittleEndian() {
    union {
        uint32_t i;
        uint8_t c[4];
    } u = {0x12345678};
    return u.c[0] == 0x78;  // 小端:低位字节在低地址
}

int main() {
    if (isLittleEndian()) {
        printf("This machine is little-endian.\n");
    } else {
        printf("This machine is big-endian.\n");
    }
    return 0;
}

4. 联合体 vs 结构体内存对比

struct S { int a; char b; double c; };  // sizeof ≈ 16(对齐后)
union U { int a; char b; double c; };   // sizeof = 8(double 最大)

printf("sizeof(struct S) = %zu\n", sizeof(struct S));
printf("sizeof(union U) = %zu\n", sizeof(union U));

三、常见应用场景总结

场景 推荐类型
表示一个对象的多个属性(如学生信息) struct
节省内存,多个变量不会同时使用 union
网络/硬件协议解析(固定内存布局) struct + #pragma pack
实现动态类型(带标签) union + enum
位域操作(节省空间) struct 中的位域(bit-field)

补充:位域示例

struct Flags {
    unsigned int is_ready : 1;   // 1 bit
    unsigned int is_valid : 1;   // 1 bit
    unsigned int count    : 6;   // 6 bits (共1字节)
};

四、注意事项

  • 结构体:成员独立存储,总大小 ≥ 各成员之和(考虑对齐)。
  • 联合体:所有成员共享内存,总大小 = 最大成员大小。
  • 联合体不能直接整体赋值(C99 起支持初始化和赋值,但需谨慎)。
  • 使用联合体时务必清楚当前有效成员,避免未定义行为。

如果你有特定应用场景(如嵌入式、协议解析、图形编程等),我可以提供更针对性的示例!