Skip to main content

8-1 struct

1. 自訂型別 struct

在 C++ 中,我們可以把多個彼此相關的資料包在一起,創造出一個全新的型別。

例如,一位學生可能有以下資料:

  • 姓名
  • 年齡
  • 成績

如果分別用三個變數來表示,資料容易分散,必須想辦法維持追蹤同一個學生的姓名、年齡和成績。

struct 是一種自訂型別,可以把多個彼此相關的資料包在一起,創造出一個全新的型別。

struct Student {
    string name;
    int age;
    double score;
};

這表示我們建立了一個名為 Student 的型別,它裡面有三個成員:name、age、score。


2. 為什麼需要 struct

struct 很適合用來表示「一筆完整資料」,例如:

  • 學生資料
  • 座標點
  • 商品資訊
  • 日期時間

學會 struct 之後,你會開始從「很多零散變數」進步到「有結構的資料設計」,這是寫大型程式的重要基礎。


3. 基本語法

宣告

struct Person {
    std::string name;
    int age;
};

語法重點:

  • struct 是關鍵字
  • Person 是型別名稱
  • 大括號裡面放的是成員變數
  • 最後要有分號 ;

4. 建立變數與存取成員

宣告完 struct 之後,就可以像一般型別一樣建立變數。

Person p1;

使用 . 來存取成員:

p1.name = "Alice";
p1.age = 18;
範例:
#include <iostream>

using namespace std;

struct Person {
    string name;
    int age;
};

int main() {
    Person p1;
    p1.name = "Alice";
    p1.age = 18;

    cout << "Name: " << p1.name << endl;
    cout << "Age: " << p1.age << endl;

    return 0;
}

執行結果:

Name: Alice Age: 18

5. 初始化(給定初值)

除了先宣告再指定值,也可以在建立時直接初始化。

Person p1 = {"Bob", 20};

這相當於:

  • p1.name = "Bob"
  • p1.age = 20
Person p2 = {"Cindy", 22};
cout << p2.name << ", " << p2.age << endl;

6. struct 用於函數的參數

你可以把整個 struct 傳進函式,讓程式更清楚。

#include <iostream>

using namespace std;

struct Person {
    string name;
    int age;
};

void printPerson(Person p) {
    cout << "Name: " << p.name << endl;
    cout << "Age: " << p.age << endl;
}

int main() {
    Person p = {"David", 25};
    printPerson(p);
  
    return 0;
}

由於函數預設是以 傳值呼叫(call by value) 方式將參數傳入,如果該 struct 裡的資料很大,例如包含學生照片,通常會改用 傳參考呼叫(call by reverence),避免整份資料被複製:

void printPerson(Person& p) {
    cout << "Name: " << p.name << endl;
    cout << "Age: " << p.age << endl;
}

由於傳參考會讓被傳入的外部 struct 變數,可以在函數內被修改。若要確保不被修改,可以像這檥在參數前加上 const 標示其為常數。

void printPerson(const Person& p) {
    cout << "Name: " << p.name << endl;
    cout << "Age: " << p.age << endl;
}

7. 陣列中的 struct

struct 很常和陣列搭配使用,用來儲存多筆同類型資料。

#include <iostream>

using namespace std;

struct Student {
    string name;
    int score;
};

int main() {
    Student students[3] = {
        {"Amy", 90},
        {"Brian", 85},
        {"Chris", 92}
    };

    for (int i = 0; i < 3; i++)
    {
        cout << students[i].name << ": " << students[i].score << endl;
    }

    return 0;
}

這種寫法在成績系統、通訊錄、商品清單中都很常見。


8. 巢狀 struct

struct 裡面還可以再放另一個 struct。

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

struct Student {
    string name;
    Date birthday;
};

使用方式:

Student s;
s.name = "Helen";
s.birthday.year = 2005;
s.birthday.month = 8;
s.birthday.day = 15;

這讓複雜資料可以分層整理,結構更清楚。


9. 指標與 struct

如果你有一個指向 struct 的指標,在存取其成員時要用 -> 而不是 .。

Person p = {"Ivy", 21};
Person* ptr = &p;

cout << ptr->name << endl;
cout << ptr->age << endl;

比較

p.name.       // 物件存取成員
(*ptr).name   // 物件存取成員
ptr->name     // 指標存取成員

10. 一個完整綜合範例

#include <iostream>

using namespace std;

struct Student {
    string name;
    int age;
    double score;

    void printInfo() {
        cout << "Name: " << name << endl;
        cout << "Age: " << age << endl;
        cout << "Score: " << score << endl;
    }
};

int main() {
    Student s1 = {"Jack", 18, 95.5};
    s1.printInfo();

    return 0;
}

這個例子同時用到了:

  • struct 宣告
  • 成員變數
  • 初始化
  • 成員函式
  • 物件呼叫函式

11. struct 的使用時機

當你遇到以下情境時,可以考慮使用 struct:

  • 一筆資料有多個欄位
  • 這些欄位彼此有關聯
  • 你希望程式更容易閱讀與維護

例如:

  • Point:座標 (x, y)
  • Book:書名、作者、價格
  • Employee:姓名、編號、薪資

12. 小練習小專案練習:

練習 1專案背景

請設計一個 Book 的 struct,包含:你要做一個小型的收銀系統。系統會:

  • 書名建立一張發票(含建立時間與稅率)
title掃描商品(品名與價格) 若商品重複出現,數量要累加 計算未稅金額與含稅總金額 最後列印完整發票

你會學到什麼

    使用 struct 表示資料模型(Item、Date、Time、Invoice) 用函式分工(prepareInvoice、scanItem、printInvoice) 使用固定大小陣列儲存品項 在迴圈中搜尋重複資料 更新數量與金額 使用 C++ 標準函式取得目前日期時間

    專案檔案

      main.cpp:主要程式 in.txt:測試輸入資料(可用重導向測試)

      功能需求

        初始化發票
          建立空白發票 讀取目前日期與時間 設定稅率(例如 0.05) amount 初始為 0 totalDue 初始為 amount * (1 + taxRate)
            掃描商品
              每次輸入一筆:name price 若 name 已存在:
                quantity + 1 amount 只加上本次 price 作者若 authorname 不存在:
                  新增一筆 Item{name, price, 1} amount 加上 price 價格每次更新 amount 後都要重算 totalDue
                    列印發票
                      顯示日期與時間 顯示所有品項(品名、價格、數量) 顯示 amount、taxRate、totalDue

                      建議輸入格式

                      第一行輸入整數 n(要掃描幾筆) 接著輸入 n 行,每行一筆:

                        name price

                        並建立一個變數,輸出它的內容。範例:

                        10
                        Apple 35.5
                        Banana 12
                        Milk 45
                        Bread 30
                        Apple 35.5
                        Eggs 68
                        Coffee 120
                        Bread 30
                        Orange 25
                        Milk 45
                        

                        練習建議輸出格式

                        Date: 2026/4/20
                        Time: 9:45:6
                        ------------------------------
                        Items:
                        - Apple: $35.5 x 2
                        - Banana: $12 x 1
                        - Milk: $45 x 2
                        - Bread: $30 x 2
                        - Eggs: $68 x 1
                        - Coffee: $120 x 1
                        - Orange: $25 x 1
                        ------------------------------
                        Amount: $556.5
                        Tax Rate: 5%
                        Total Due: $584.325
                        

                        如何測試

                        請設計一個你可以用輸入重導向測試:

                        Pointmain.exe < in.txt
                        
                        的 struct,包含:

                        驗收標準

                        • x 程式可成功編譯執行
                        • 重複品項會正確累加 yquantity
                        • amount 不會因重複品項而重複倍算
                        totalDue 計算正確(amount * (1 + taxRate)) 發票輸出格式清楚可讀

                        再寫一個函式

                        提示

                        printPoint()
                        來印出座標。先完成 prepareInvoice,再做 scanItem,最後做 printInvoice scanItem 先用 for 迴圈找同名品項 找到就更新並 return,沒找到才新增 注意陣列上限(itemList 最多 100 筆) 若輸入失敗(cin 失敗)要考慮防呆

                        練習 3額外挑戰

                        請建立一個

                        Student固定小數位數 陣列,存放(如:小數以下兩位) 3若同名商品出現不同價格,定義你自己的規則(拒絕、覆蓋、或視為不同品項) 位學生的姓名與分數,並用迴圈印出所有資料。顯示小計欄位(price * quantity) 將稅金獨立列印(tax = totalDue - amount) 改用 vector 取代固定長度陣列 - 需使用到還沒教的 STL 容器 vector 支援含空白的品名(例如 "Green Tea") - 需使用到還沒教的 getline 函數