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

C語言結構體深度解析:定義、嵌套、指針操作與函數傳參

     編輯:關於C語言

C語言結構體深度解析:定義、嵌套、指針操作與函數傳參

結構體是 C 語言構建複雜數據類型的基石,允許將不同類型的數據組合成一個邏輯整體。本文系統講解結構體的定義方式、數組應用、指針操作、函數傳參語義、嵌套定義及枚舉類型。

一、結構體類型定義與變量創建

三種定義變量的方式

// 方式1:分離定義類型與變量(推薦,可復用類型)
struct Student {
    int age;
};
struct Student stu;

// 方式2:定義類型同時定義變量
struct Student {
    int age;
} stu;
struct Student stu2; // 可繼續使用類型名定義

// 方式3:匿名結構體(無法復用類型名)
struct {
    int age;
} stu;

初始化示例

struct Person {
    int age;
    double height;
    char *name;
};

int main() {
    // 完全初始化
    struct Person p1 = {20, 1.55, "jack"};
    
    // 指定成員初始化(C99),順序可打亂
    struct Person p2 = {.height = 1.78, .name = "jim", .age = 30};
    
    // 錯誤:定義後不能用花括號賦值
    // struct Person p3; p3 = {30, 1.67, "jake"}; // 編譯錯誤
    
    return 0;
}

二、結構體數組

struct RankRecord {
    int no;
    char *name;
    int score;
};

int main() {
    struct RankRecord records[3] = {
        {1, "jack", 5000},
        {2, "jim", 500},
        {3, "jake", 300}
    };
    
    for (int i = 0; i < 3; i++) {
        printf("%d\t%s\t%d\n", records[i].no, records[i].name, records[i].score);
    }
    return 0;
}

三、指向結構體的指針

定義與訪問成員的兩種語法

struct Student {
    int no;
    int age;
};

int main() {
    struct Student stu = {1, 20};
    struct Student *p = &stu;
    
    // 方式1:解引用後用點操作符
    (*p).age = 30;
    
    // 方式2:箭頭操作符(推薦,更直觀)
    p->age = 30;
    p->no = 2;
    
    printf("age=%d, no=%d\n", p->age, p->no);
    return 0;
}

四、結構體作為函數參數:值傳遞 vs 地址傳遞

這是 C 語言函數調用語義的核心區別:

傳遞方式語法內存行為能否修改實參
值傳遞void test(struct Student s)拷貝所有成員到形參,佔用棧空間不能,修改只影響副本
地址傳遞void test(struct Student *p)傳遞指針,形參指向實參能,通過 p->member 修改實參

代碼演示三種調用效果

#include <stdio.h>

struct Student { int age; int no; };

// 值傳遞:不影響實參
void test_val(struct Student s) { s.age = 30; s.no = 2; }

// 地址傳遞:影響實參
void test_ptr(struct Student *p) { p->age = 15; p->no = 2; }

// 指針傳遞但修改指針指向:不影響實參(指針本身是值傳遞)
void test_ptr_rebind(struct Student *p) {
    struct Student stu2 = {15, 2};
    p = &stu2;        // 只改變局部指針 p 的指向
    p->age = 16;      // 修改的是 stu2,而非 main 中的 stu
}

int main() {
    struct Student stu = {28, 1};
    
    test_val(stu);
    printf("值傳遞後: age=%d, no=%d\n", stu.age, stu.no); // 28, 1
    
    test_ptr(&stu);
    printf("地址傳遞後: age=%d, no=%d\n", stu.age, stu.no); // 15, 2
    
    test_ptr_rebind(&stu);
    printf("重綁指針後: age=%d, no=%d\n", stu.age, stu.no); // 15, 2(未變)
    
    return 0;
}

五、結構體嵌套定義

結構體成員可以是另一個結構體類型,實現層級數據建模:

struct Date {
    int year, month, day;
};

struct Student {
    int no;
    struct Date birthday;
    struct Date enrollDate;
};

int main() {
    struct Student stu = {1, {2000, 9, 10}, {2012, 9, 10}};
    printf("生日: %d-%d-%d\n", stu.birthday.year, stu.birthday.month, stu.birthday.day);
    return 0;
}

注意:結構體定義內不能直接嵌套自身類型的變量(會導致無限遞歸大小),但可以嵌套指向自身類型的指針(用於鏈表、樹等結構)。

六、枚舉類型

枚舉本質是整型常量的別名,增強代碼可讀性:

enum Sex { Man, Woman, Unknown }; // 默認 0, 1, 2

enum Season {
    Spring = 1,  // 指定起始值
    Summer,      // 2
    Autumn,      // 3
    Winter       // 4
};

int main() {
    enum Season s = 100000; // C語言弱類型,不報錯,但語義錯誤
    printf("%d\n", s);      // 輸出 100000
    return 0;
}

最佳實踐:配合 switch 使用枚舉,編譯器可提示未處理的情況;避免將枚舉作為任意整數使用。

七、總結與建議

  • 優先使用「分離定義類型與變量」的方式,便於復用類型名。
  • 大型結構體傳參務必使用指針,避免棧拷貝開銷。
  • 函數需修改實參結構體時,參數類型為 struct Type *;僅讀取時可用 const struct Type *。
  • 嵌套結構體建模實體關係(如學生-生日),語義清晰且內存連續。
  • 枚舉用於有限離散值集合,配合 switch 實現窮盡性檢查。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved