Back
Clang & Wasm 教學

結構體與聯合體入門

從基礎語法到實際應用 - 掌握資料組織的核心技巧與記憶體管理方法


1. 結構體基礎概念

1.1 為什麼需要結構體?

  • 生活案例
    • 單一變數 → 便利貼(只能記一件事)
    • 結構體 → 學生證(整合姓名、學號、照片)
  • 程式碼對比
    // 舊方法:分散變數
    char name[50];
    int student_id;
    float score;
    
    // 新方法:結構體整合
    struct student {
        char name[50];
        int student_id;
        float score;
    };
    

1.2 定義與初始化

// 定義「座標」結構體
struct point {
    int x;
    int y;
};

// 初始化方式
struct point p1 = {10, 20};               // 順序初始化
struct point p2 = {.y=30, .x=5};          // 指定成員初始化(C99標準)
struct point p3;                          // 先宣告後賦值
p3.x = 100; p3.y = 200;

1.3 存取成員

printf("p1座標: (%d, %d)\n", p1.x, p1.y);  // 點運算子存取
p1.x += 5;                                // 修改成員值

2. 結構體指標

2.1 指標基礎回顧

int num = 10;
int *ptr = #          // 指向整數的指標
printf("%d", *ptr);       // 用*取值

2.2 結構體指標操作

struct student s1 = {"Alice", 410530, 90.5};
struct student *s_ptr = &s1;

// 兩種存取方式對比
printf("Name: %s\n", (*s_ptr).name);  // 括號不可省略!
printf("Name: %s\n", s_ptr->name);    // 推薦用箭頭運算子

2.3 結構體指標實戰

// 函數參數傳遞結構體指標(避免複製開銷)
void print_student(const struct student *s) {
    printf("Student ID: %d\n", s->student_id);
}

int main() {
    struct student s1 = {"Bob", 410531, 88.0};
    print_student(&s1);  // 傳遞地址
    return 0;
}

3. 綜合範例

3.1 設計「手機」結構體

struct smartphone {
    char model[20];
    int ram;      // GB
    float price;
};

// 1. 宣告兩支手機
// 2. 寫一個函數用指標參數印出手機資訊
void print_phone(const struct smartphone *phone) {
    printf("Model: %s\n", phone->model);
    // 補完其他資訊打印
}

3.2 想一想

struct book {
    char title[50];
    int pages;
};
struct book b1 = {"C Programming", 300};
struct book *b_ptr = &b1;

// 提問:以下程式碼輸出什麼?
printf("%d", b_ptr->pages + 10);

Clang編譯小技巧

clang -Wall -Wextra struct_practice.c  # 開啟所有警告

4. 結構體陣列

4.1 靜態結構體陣列

// 定義「學生」結構體陣列
struct student {
    char name[20];
    int id;
    float score;
};

// 初始化全班資料(台灣學號範例)
struct student class[3] = {
    {"Alice", 410530, 88.5},
    {"Bob", 410531, 76.0},
    {"Carol", 410532, 92.3}
};

// 計算全班平均分數
float avg = 0;
for(int i = 0; i < 3; i++) {
    avg += class[i].score;
}
printf("平均分數: %.1f\n", avg/3);

4.2 結構體陣列與指標

// 用指標遍歷結構體陣列
struct student *ptr = class;  // 陣列名稱即為首元素地址
for(int i = 0; i < 3; i++) {
    printf("%s的學號: %d\n", (ptr+i)->name, (ptr+i)->id);
}

5. 巢狀結構

5.1 基本巢狀結構

// 定義「日期」與「學生」結構體
struct date {
    int year;
    int month;
    int day;
};

struct student {
    char name[20];
    struct date birthday;  // 巢狀結構
};

// 初始化與存取
struct student s1 = {
    "David", 
    {2000, 9, 15}  // 巢狀初始化
};
printf("%s的出生年份: %d\n", s1.name, s1.birthday.year);

5.2 實際應用:圖書管理系統

struct publisher {
    char name[50];
    char country[20];
};

struct book {
    char title[100];
    struct publisher pub;  // 巢狀結構
    float price;
};

// 出版社範例
struct book b1 = {
    "山賊王",
    {"西立", "台北"},
    580.0
};

6. 聯合體(union)入門

聯合體通常用於實現記憶體共享

6.1 聯合體基本特性

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

// 記憶體共享示範
union data d1;
d1.i = 10;
printf("整數值: %d\n", d1.i);  // 輸出10

d1.f = 3.14;
printf("浮點數值: %f\n", d1.f);  // 輸出3.14
// 此時d1.i的值已被覆蓋!

6.2 聯合體與結構體混用

union mix {
    int arr[3];
    struct {
        int one;
        int two;
        int three;
    } splite;
};

union mix t;
t.arr[0] = 500;
t.arr[1] = 600;
t.arr[2] = 700;

printf("one: %d\n", t.splite.one);
printf("two: %d\n", t.splite.two);
printf("three: %d\n", t.splite.three);

7. 綜合練習

任務:建立「多邊形」資料結構

struct point {
    int x;
    int y;
};

struct polygon {
    char name[20];
    int sides;
    struct point vertices[10];  // 頂點座標陣列
};

8. 結構體與函數指標

8.1 函數指標成員

// 定義計算器結構體
struct calculator {
    char name[20];
    float (*operation)(float, float);  // 函數指標成員
};

// 定義運算函數
float add(float a, float b) { return a + b; }
float multiply(float a, float b) { return a * b; }

int main() {
    struct calculator calc_add = {"加法器", add};
    struct calculator calc_mul = {"乘法器", multiply};
    
    printf("3 + 5 = %.1f\n", calc_add.operation(3, 5));
    printf("3 * 5 = %.1f\n", calc_mul.operation(3, 5));
    return 0;
}

8.2 實際應用:策略模式

// 台灣天氣數據處理範例
struct weather_analyzer {
    char location[20];
    float (*analyze)(float[], int);  // 分析策略
};

float avg_temperature(float temps[], int n) { /*...*/ }
float max_temperature(float temps[], int n) { /*...*/ }

struct weather_analyzer taichung = {
    "台中",
    avg_temperature  // 使用平均溫度策略
};

9. 動態結構體操作

9.1 結構體與動態陣列

struct student {
    char name[20];
    int id;
};

// 動態建立學生陣列
int n = 5;
struct student *class = malloc(n * sizeof(struct student));

// 初始化資料
for(int i=0; i<n; i++) {
    sprintf(class[i].name, "學生%d", i+1);
    class[i].id = 410530 + i;
}

free(class);  // 釋放記憶體

10. 聯合體的進階應用

10.1 型別轉換技巧

// 檢視float的記憶體表示
union float_converter {
    float value;
    unsigned char bytes[4];
} fc;

fc.value = 3.14;
printf("IEEE754表示: ");
for(int i = 0; i < 4; i++) {
    printf("%02X ", fc.bytes[i]);  // 輸出16進位表示
}

10.2 節省記憶體應用

// 台灣便利商店商品資料
struct product {
    char name[50];
    union {
        int quantity;  // 一般商品
        float weight;  // 散裝商品
    } stock;
    char type;  // 'Q'=數量, 'W'=重量
};

struct product p1 = {"茶葉蛋", .stock.quantity=10, 'Q'};
struct product p2 = {"餅乾", .stock.weight=150.5, 'W'};

11. 綜合範例

悠遊卡交易系統模擬

struct transaction {
    struct date time;
    char location[30];
    union {
        int bus_route;    // 公車路線
        char station[20]; // 捷運站名
    } transport;
    int amount;
};

12. C語言結構體與聯合體重點總結

  1. 結構體 (struct)

    • 打包不同類型變數
    • .存取變數成員,->存取指標成員
    • 可巢狀定義,建立複雜資料結構
  2. 聯合體 (union)

    • 共享記憶體空間
    • 同一時間只能使用一個成員
    • 節省記憶體,用於型別轉換

13. 課堂作業

圓柱與方柱體積計算程式

作業要求

  1. 設計結構體包含:
    • 圓柱(半徑、高)
    • 方柱(長、寬、高)
  2. 使用聯合體儲存不同柱體的參數
  3. 使用指標操作柱體資料
  4. 依序輸入2種柱體的參數後,自動計算並顯示體積和表面積

程式碼 架構

#include <stdio.h>

#define PI 3.14159

// 聯合體儲存柱體參數
union ColumnData {
    struct { float radius, height; } cylinder;  // 圓柱
    struct { float length, width, height; } prism; // 方柱
};

// 主結構體
typedef struct {
    int type; // 1=圓柱, 2=方柱
    union ColumnData data;
} Column;

// 計算體積
float volume(Column *col) {
    if(col->type == 1) { // 圓柱
        ...
    }
    else { // 方柱
        ...
    }
}

// 計算表面積
float surface_area(Column *col) {
    ...
}

int main() {
    Column columns[2]; // 儲存2種柱體
    
    // 輸入圓柱參數
    printf("請輸入圓柱的半徑和高度: ");
    ...
    
    // 輸入方柱參數
    printf("請輸入方柱的長、寬、高: ");
    ...
    
    // 計算並輸出結果
    printf("\n計算結果:\n");
    ...
    
    return 0;
}

執行範例

請輸入圓柱的半徑和高度: 3 5
請輸入方柱的長、寬、高: 2 3 4

計算結果:
1. 圓柱: 體積=141.37, 表面積=150.80
2. 方柱: 體積=24.00, 表面積=52.00
SNPQ © 2025