首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C語言結構體全解析:定義、嵌套、數組、指針、對齊與模塊化實戰

     編輯:關於C語言

結構體:自定義複合數據類型的基石

結構體(struct)將不同類型的變量組合為單一實體,是 C 語言實現抽象數據類型(ADT)的核心機制。本文系統覆蓋定義、初始化、嵌套、數組、指針操作、內存對齊、模塊化拆分與工程實踐。

基礎定義與實例化

// 1. 標準定義 + 類型別名
typedef struct {
    char name[32];
    int age;
    float score;
    struct { int y, m, d; } birthday;  // 匿名嵌套結構體
} Student;

// 2. 多種初始化方式
Student s1 = {"Alice", 20, 92.5, {2004, 5, 17}};          // 順序初始化
Student s2 = {.name="Bob", .score=88.0, .age=21};         // C99 指定初始化器
Student s3 = s1;                                          // 整體賦值(淺拷貝)

核心操作詳解

1. 成員訪問:. vs ->

場景語法本質
結構體變量s.age直接偏移訪問
結構體指針p->age 等價 (*p).age解引用後偏移

2. 結構體數組:批量管理同類實體

Student class[3] = {
    {"Zhang", 23, 90, {1990,9,28}},
    {"Zuo",   25, 88, {1991,9,20}},
    {"Fang",  24, 89, {1992,8,8}}
};

// 遍歷、查找最高分、最小年齡
Student *max = &class[0], *min = &class[0];
for (int i=1; i<3; ++i) {
    if (class[i].score > max->score) max = &class[i];
    if (class[i].age < min->age) min = &class[i];
}

3. 結構體指針與動態分配

Student *p = malloc(sizeof *p);
if (p) {
    strcpy(p->name, "Dynamic");
    p->age = 22;
    free(p);
}

// 鏈表節點自引用
typedef struct Node {
    int data;
    struct Node *next;  // 必須用 struct Node*,typedef 名尚未生效
} Node;

4. 結構體作為函數參數:傳值 vs 傳指針

// 傳值:拷貝整個結構體,修改不影響原值,大結構體開銷大
void printByVal(Student s) { printf("%s\n", s.name); }

// 傳指針:傳遞地址,可修改原值,高效,推薦常用
void printByPtr(const Student *s) { printf("%s\n", s->name); }
void modify(Student *s) { s->score += 5; }

內存佈局與對齊:sizeof 不等於成員大小之和

struct Demo {
    char c;      // 1 byte
    // 3 bytes padding
    int i;       // 4 bytes
    // 0 bytes padding
    double d;    // 8 bytes
};
// sizeof(Demo) = 24 (非 13)
// 對齊規則:每個成員偏移為其類型大小的倍數,結構體總大小為最大對齊數的倍數

優化技巧:成員重排節省空間

// 優化前 24 bytes
struct Bad { char c; int i; double d; };

// 優化後 16 bytes(double 8 對齊,int 4,char 1+3 padding)
struct Good { double d; int i; char c; };

模塊化實戰:多文件拆分

文件結構

student/
├── include/
│   └── student.h     // 類型定義、接口聲明
├── src/
│   ├── student.c     // 實現
│   └── sort.c        // 排序算法
└── main.c

student.h

#ifndef STUDENT_H
#define STUDENT_H

typedef struct {
    char name[32];
    int age;
    float score;
    struct { int y, m, d; } birthday;
} Student;

// 接口聲明
void student_print(const Student *s);
void student_sort_by_score(Student *arr, int n);  // 降序
int student_find_top(const Student *arr, int n);  // 返回最高分索引

#endif

student.c

#include "student.h"
#include 
#include 

void student_print(const Student *s) {
    printf("%s, %d, %.2f, %04d-%02d-%02d\n",
           s->name, s->age, s->score,
           s->birthday.y, s->birthday.m, s->birthday.d);
}

void student_sort_by_score(Student *arr, int n) {
    // 簡單選擇排序,實際可用 qsort
    for (int i=0; i arr[max_idx].score) max_idx = j;
        if (max_idx != i) {
            Student tmp = arr[i];
            arr[i] = arr[max_idx];
            arr[max_idx] = tmp;
        }
    }
}

進階模式:不透明指針(Opaque Pointer / Pimpl)

隱藏結構體定義,僅在 .c 中暴露,頭文件只聲明前向標籤,實現真正的封裝與編譯防火牆。

// api.h
typedef struct Context Context;  // 前向聲明
Context* ctx_create(void);
void ctx_destroy(Context *c);
int ctx_do_work(Context *c, int input);

// api.c
#include "api.h"
struct Context {  // 完整定義隱藏在 .c
    int state;
    double cache[1024];
};
Context* ctx_create() { return calloc(1, sizeof(struct Context)); }
void ctx_destroy(Context *c) { free(c); }
int ctx_do_work(Context *c, int input) { return c->state += input; }

常見錯誤與避坑指南

  1. 柔性數組成員:C99 允許結構體最後一個成員為不定長數組 char data[];,配合 malloc(sizeof(Struct) + len) 實現變長結構體
  2. 位域:unsigned int flag:1; 節省空間,但不可取地址、跨平台佈局不可移植
  3. 結構體比較:不能直接用 ==(含 padding 未定義值),需逐成員比較或 memcmp(需先置零 padding)
  4. 自我引用:struct Node { struct Node *next; }; 必須用標籤名,typedef 別名在定義完成前不可用
  5. 對齊屬性:GCC/Clang 支持 __attribute__((packed)) 強制 1 字節對齊,網絡協議/硬件寄存器映射常用

總結

結構體是 C 語言從「數據+函數」走向「面向對象雛形」的關鍵。精通定義/初始化、指針操作、內存對齊、模塊化拆分、不透明指針封裝,配合const 正確性、所有權語義(誰分配誰釋放)、錯誤碼返回,即可構建健壯、可維護的系統級代碼庫。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved