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

C語言結構體與鏈表完全指南:從定義、內存對齊到動態數據結構實戰

     編輯:C 語言入門

第一部分:結構體體系深度解析

1. 結構體類型定義三式

// 1. 先定類型,後定變量
struct Student { char name[20]; int age; float score; };
struct Student s1, s2;

// 2. 定類型同時定變量
struct Teacher { char name[20]; int id; } t1, t2;

// 3. 匿名結構體(僅能定變量,無法復用類型)
struct { char name[20]; int age; } anon1, anon2;

2. 成員訪問與嵌套

struct Date { int y, m, d; };
struct Person { char name[20]; struct Date birthday; };

struct Person p = {"Tom", {2000, 5, 1}};
p.birthday.m = 6;           // 多級成員訪問
struct Person *pp = &p;
pp->birthday.m = 7;         // 指針訪問嵌套成員

3. 內存對齊與大小計算 —— 面試高頻考點

struct Demo {
    char c1;     // 偏移 0, 大小 1
    // 3 字節填充
    int i;       // 偏移 4, 大小 4
    char c2;     // 偏移 8, 大小 1
    // 3 字節填充(結構體大小須為最大成員 4 的倍數)
};               // sizeof = 12

// 驗證
printf("%zu\n", sizeof(struct Demo)); // 12
printf("%zu\n", offsetof(struct Demo, i)); // 4

對齊規則:

  1. 首成員偏移 0。
  2. 後續成員偏移 = min(該類型大小, 編譯器對齊數) 的倍數(通常 = 類型大小)。
  3. 結構體總大小 = 最大成員大小的整數倍(尾部填充)。
  4. #pragma pack(n) 可壓縮對齊(n=1 為緊湊),但可能降低訪問性能或導致硬件異常(某些架構)。

4. 結構體與函數交互三模式

模式原型特點適用場景
值傳遞void print(struct Student s)拷貝整個結構體,開銷大,不修改原值小型結構體(<16 字節),只讀
指針傳遞void print(const struct Student *p)傳地址,無拷貝,可修改(無 const 時)通用首選,大結構體、需修改
返回結構體struct Student input(void)C99 支持直接返回,編譯器優化(RVO/NRVO)工廠函數、輸入封裝

5. 結構體數組:批量管理同質對象

struct Student class[50] = {
    {101, "Zhang", 88.5},
    {102, "Li", 92.0}
};
// 遍歷統計
for (int i = 0; i < 50; ++i)
    if (class[i].score > 90) printf("%s\n", class[i].name);

第二部分:鏈表——動態數據結構核心

1. 節點定義與基本操作

struct Node {
    int data;
    struct Node *next;
};

// 創建節點
struct Node *create_node(int val) {
    struct Node *p = malloc(sizeof(*p));
    if (!p) return NULL;
    p->data = val;
    p->next = NULL;
    return p;
}

// 尾插法建表(帶頭節點)
struct Node *build_list(int *arr, int n) {
    struct Node head = {0, NULL}, *tail = &head;
    for (int i = 0; i < n; ++i) {
        tail->next = create_node(arr[i]);
        tail = tail->next;
    }
    return head.next; // 返回首數據節點
}

// 遍歷
void print_list(struct Node *head) {
    for (struct Node *p = head; p; p = p->next)
        printf("%d -> ", p->data);
    puts("NULL");
}

// 查找
struct Node *find(struct Node *head, int val) {
    for (struct Node *p = head; p; p = p->next)
        if (p->data == val) return p;
    return NULL;
}

// 插入(在 p 後插入 new_node)
void insert_after(struct Node *p, struct Node *new_node) {
    new_node->next = p->next;
    p->next = new_node;
}

// 刪除 p 後節點
void delete_after(struct Node *p) {
    if (p->next) {
        struct Node *tmp = p->next;
        p->next = tmp->next;
        free(tmp);
    }
}

// 銷毀鏈表
void destroy_list(struct Node *head) {
    while (head) {
        struct Node *tmp = head;
        head = head->next;
        free(tmp);
    }
}

2. 雙向鏈表節點

struct DNode {
    int data;
    struct DNode *prev, *next;
};
// 支持 O(1) 反向遍歷、任意節點刪除(無需前驅)

3. 鏈表 vs 結構體數組 對比

維度結構體數組鏈表
內存分配連續,編譯期/啟動期固定大小離散,運行期動態申請/釋放
插入刪除O(n) 移動元素O(1) 指針重接(已知位置)
隨機訪問O(1) 下標O(n) 遍歷
內存開銷無額外指針每節點 +1/2 指針
緩存友好極佳(空間局部性)差(指針跳躍)
適用場景大小固定、高頻隨機訪問大小動態、高頻增刪、未知上限

第三部分:實戰項目——學生管理系統(鏈表版)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct Student {
    int id;
    char name[20];
    float score;
    struct Student *next;
};

struct Student *head = NULL;

// 增
void add_student(int id, const char *name, float score) {
    struct Student *p = malloc(sizeof(*p));
    p->id = id; strcpy(p->name, name); p->score = score;
    p->next = head; head = p; // 頭插
}

// 刪
bool del_student(int id) {
    struct Student **pp = &head; // 指向指針的指針,統一處理頭節點
    while (*pp) {
        if ((*pp)->id == id) {
            struct Student *tmp = *pp;
            *pp = tmp->next;
            free(tmp);
            return true;
        }
        pp = &(*pp)->next;
    }
    return false;
}

// 查
struct Student *find_student(int id) {
    for (struct Student *p = head; p; p = p->next)
        if (p->id == id) return p;
    return NULL;
}

// 改
bool update_score(int id, float new_score) {
    struct Student *p = find_student(id);
    if (p) { p->score = new_score; return true; }
    return false;
}

// 遍歷輸出
void list_all(void) {
    for (struct Student *p = head; p; p = p->next)
        printf("ID:%d Name:%s Score:%.1f\n", p->id, p->name, p->score);
}

int main(void) {
    add_student(1, "Alice", 90);
    add_student(2, "Bob", 85);
    list_all();
    update_score(1, 95);
    del_student(2);
    list_all();
    // 程序結束前釋放
    while (head) { struct Student *t = head; head = head->next; free(t); }
    return 0;
}

總結

  • 結構體是異構數據聚合,內存對齊決定大小,指針傳遞是函數交互主流。
  • 鏈表用指針串聯離散節點,實現動態增刪,犧牲隨機訪問換取靈活性。
  • 雙重指針(**pp)是鏈表刪除/插入統一處理頭節點的神級技巧。
  • 實戰中:小規模固定集合用數組,動態不定集合用鏈表,高性能場景考慮內存池+鏈表或跳表/B+樹。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved