C 語言自學攻略-enum、struct、union 與 typedef 的深入探討

更新 發佈閱讀 17 分鐘

本節我們來講一些資料型別的使用,涵蓋enum, struct, union, 以及一個好用的搭配關鍵字typedef 讓命名更方便、程式碼更易讀。

enum

enum 裡面是一組具名的的整數常數,透過enum 具名的整數常數來表達數值,適合用來定義有意義的狀態。

基本宣告與定義

enum action{Up, Down, Left, Right, Stop}; //預設第一個具名的常數即Up為數字0,後面的常數為Down=1, Left=2, Right=3, Stop=4

若要宣告並定義一個enum型別的變數,語法如下:

action act(變數名稱) = Up;

或者是一開始宣告完enum後直接添加變數名稱,語法如下:

enum action2{Up2=1, Down2, Left2, Right2, Stop2}act2;
act2 = Stop;

若是像下面這樣,裡面的數值常數為 {0, 1, 100, 101, 102}

enum action2{Up2, Down2, Left2=100, Right2, Stop2}act2;
act2 = Stop;

接著就可以輸出數值測試了:

#include <stdio.h>

int main() {
// 定義 action 枚舉,名稱不變
enum action {Up, Down, Left, Right, Stop};
enum action act = Up; // 使用 action 中的 Up

// 改變 action2 中的枚舉名稱,避免與 action 中的名稱重複
enum action2 {Up2=1, Down2, Left2, Right2, Stop2}act2;
act2 = Stop2; // 使用 action2 中的 Stop2

printf("act = %d\n", act); // 輸出 Up (0)
printf("act2 = %d\n", act2); // 輸出 Stop2 (5)

return 0;
}

實際案例

#include<stdio.h>
int main(){
// 定義了顏色列舉類型 (red = 1, green = 2, blue = 3)
enum color { red=1, green, blue };

// 宣告一個變數來儲存使用者選擇的顏色
enum color favorite_color;

// 宣告一個無號整數變數,用來暫存輸入的選項值
unsigned int ui;

// 提示使用者選擇顏色
printf("Color you like: (1. red, 2. green, 3. blue)?\n");
//scanf("%u",&favorite_color); // It's not recommoned
// 使用 scanf 讀取整數輸入到 ui
scanf("%u", &ui);

// 將使用者的輸入轉換為列舉類型
favorite_color = (enum color)ui;

// 再使用 switch 來判斷選擇的顏色
switch (favorite_color)
{
case red:
// 如果選擇的是 red (索引 0)
printf("What you like is red.\n");
break;

case green:
// 如果選擇的是 green (索引 1)
printf("What you like is green.\n");
break;

case blue:
// 如果選擇的是 blue (索引 2)
printf("What you like is blue.\n");
break;

default:
// 輸入的選項無效 (非 1, 2, 3)
printf("Invalid choice. Please enter 1, 2, or 3.\n");
break;
}

// 結束程式
return 0;
}
  • switch中,以redgreenblue來代替單純使用數字1、2、3,這樣顯得更視覺化。

struct

struct 是一種內含不同型別的數據結構,適合用來描述複合數據,例如一個學生包含了姓名、年紀、學分數、平均成績等資訊。

基本宣告與定義

struct 結構名稱{
成員1;
成員2;
...
};
struct student{
int id;
char name[10];
};

宣告後建立變數:

struct 結構名稱 結構變數名稱;
struct student s1;

可以同時指定成員的初始值:

struct student s1 = {136, "Michael"} //利用{}賦值

也可以:

struct student{
int id;
char name[10];
}s1 = {136, "Michael"};

存取結構內的資料成員

使用.存取結構成員:

s1.id = 137
strcpy(s1.name, "Bob") //s1.name = "Bob"不行喔

結構陣列:

不難理解,就是一個陣列裡面,其元素是結構,語法如下:

struct student students[3];

存取方式如下:

//假設三位學生都有了資料,現在要輸出學生們的id
for (int i = 0; i<3; i++)
{
printf("學生%d的id是%d",i+1,students[i]);
}

結構指標

語法如下:

struct data{
int year;
int month;
int day;
};
struct data hisBirthday;
struct data *ptr;
ptr = &hisBirthday;

圖示:

raw-image

存取方式:

  1. 一般方式
(*ptr).year = 2005;
(*ptr).month = 10;
(*ptr).day = 17;
  1. 使用->
ptr -> year = 2005;
ptr -> month = 10;
ptr -> day = 17;

typedef 的使用

利用以下方式宣告結構,我們還是稍嫌麻煩,所以我們使用typedef 簡化。

struct student students[3];

使用 typedef 簡化:

typedef struct {
int id;
char name[50];
} Student;
//接著我們就可以這樣宣告
Student students[3]; // 更簡潔

如果利用typedef簡化結構指標的名稱,方法如下 :

typedef struct {
int id;
char name[50];
} *Studentptr;
//Student是結構指標的別名。

範例 :

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

// 使用 typedef 簡化結構體和指標名稱
typedef struct Student {
int id;
char name[10];
} Student, *StudentPtr;

int main() {
// 使用簡化後的結構體型別
Student s1 = {101, "Alice"};
printf("ID: %d, Name: %s\n", s1.id, s1.name);

// 使用簡化後的指標型別
StudentPtr sptr = &s1;

// 透過指標操作結構體
sptr->id = 102;
strcpy(sptr->name,"Bob");
printf("ID: %d, Name: %s\n", sptr->id, sptr->name);

return 0;
}

結構內所佔的記憶體空間

請你猜一下底下這個結構的記憶體空間為多少?

#include <stdio.h>

struct Example {
char a; // 1 byte
int b; // 4 bytes
char c; // 1 byte
};

int main() {
printf("Size of struct Example: %lu\n", sizeof(struct Example));
return 0;
}

你可能會說是1+4+1等於6Bytes,但實際上輸出會是12Bytes,為什麼呢?

  • 編譯器會將結構中的每個成員放在其最大資料成員大小倍數的地址上(例如上面例子中, int 最大,所以結構內的記憶體,通常要求 4 字節對齊,即地址必須是 4 的倍數)。
  • 若成員之間因對齊而需要插入額外的字節,這些額外的空間稱為填充字節。

所以上面程式碼實際上會輸出1+3(填充字節)+4+1+3(填充字節)。總共等於12。你可能會說,那這樣不就浪費了記憶體空間了嗎? 所以為了改善這點,我們要改變排序的策略:

先說結論: 將變數由小到大或由大到小擺放,可以改善這個情況,減少記憶體浪費。請看以下程式碼:

#include <stdio.h>

struct Example {
char a; // 1 byte
char c; // 1 byte
int b; // 4 bytes
};

int main() {
printf("Size of struct Example: %lu\n", sizeof(struct Example));
return 0;
}

這次輸出變為了8Bytes,為什麼呢?

因為現在其記憶體空間變為1+1+2(填充字節)+4,總共等於8。讀者可以記住這個原則,將變數排序,減少記憶體空間的浪費。

union

union 是一種節省空間的數據結構,變數共享相同的記憶體空間。

基本宣告與定義

union UnionName {
int field1;
float field2;
char field3[20];
};

特性:

  • 節省內存,只能同一時間儲存一個成員的值。
  • 適用於對數據形式有多樣需求但存儲要求較低的場合。
  • 不同變數使用後會覆蓋掉原來的記憶體區塊(覆蓋掉現在使用變數的資料型態的記憶體大小)

範例:

#include <stdio.h>
#include <string.h>
union Data {
int i;
float f;
char str[20];
};

int main() {
union Data data;
printf("%d\n",sizeof(data)); // 輸出20,總共20Bytes共用
data.i = 10; // 前4Bytes被使用,賦值為10
printf("Integer: %d\n", data.i);

data.f = 220.5; // 前4Bytes被使用,賦值為220.5
printf("Float: %.2f\n", data.f);

strcpy(data.str,"Hello"); // 前5Bytes被使用,賦值為Hello
printf("String: %s\n", data.str);

return 0;
}

注意:data.i 的值會被覆蓋,因為共用體共用內存。

這裡又不得不提union在記憶體中的樣子了,不過這裡我們要先談談位元組順序,位元組順序是指資料在記憶體中的放置順序,不同的 CPU 可能會採用不同的放置規則,若遇到需要在不同機器或是網路之間交換低階的二進位資料時,就必須注意這個問題。

而現今主流的CPU中,最常見的位元放置順序有兩種,分別為Little-Endian(指把最高位的位元組放在最高的記憶體位址上。)和Big-Endian(指資料放進記憶體中的時候,最高位的位元組會放在最低的記憶體位址上)。

請看圖示:

raw-image

現在下面有一段程式,請觀察一下這是哪一種位元組順序,並觀察數值覆蓋的情況:

#include <stdio.h>

int main()
{
// 定義一個 union,內部包含一個 4-byte 的整數和一個陣列
union tag {
int a; // 4 bytes 整數
char b[4]; // 字元陣列
} data; // 宣告一個名為 data 的 union 變數

// 將整數欄位初始化為 0x12345678 (十六進位表示法)
data.a = 0x12345678;

// 輸出 union 的大小,以及各欄位的大小
printf("%d\n", sizeof(data)); // union 的大小,取最大成員的大小
printf("%d\n", sizeof(data.a)); // 整數欄位的大小
printf("%d\n", sizeof(data.b)); // 字元陣列的大小
printf("\n");

// 輸出整數欄位和第一個字元的值,觀察記憶體儲存順序 (little endian)
printf("0x%x\n", data.a); // 輸出整數 a 的值
printf("0x%x\n", data.b[0]); // 輸出 b[0] 的值 (低位元組)

printf("\n");

// 輸出整個整數的值,並分別輸出每一個位元組的值
printf("0x%x\n", data.a); // 顯示整數 a
printf("0x%x\n", data.b[0]); // 第一個位元組 (最低位)
printf("0x%x\n", data.b[1]); // 第二個位元組
printf("0x%x\n", data.b[2]); // 第三個位元組
printf("0x%x\n", data.b[3]); // 第四個位元組 (最高位)

printf("\n");

// 修改 b[0] 的值,觀察對整個整數的影響
data.b[0] = 0x99; // 修改最低位元組為 0x99
printf("0x%x\n", data.a); // 再次輸出整數 a,觀察修改後的變化

data.b[1] = 0x99; // 修改第二個位元組為 0x99
printf("0x%x\n", data.a); // 再次輸出整數 a,觀察修改後的變化

data.b[2] = 0x99; // 修改第三個位元組為 0x99
printf("0x%x\n", data.a); // 再次輸出整數 a,觀察修改後的變化

data.b[3] = 0x99; // 修改第四個位元組為 0x99
printf("0x%x\n", data.a); // 再次輸出整數 a,觀察修改後的變化

return 0;
}
/* 輸出
4
4
4

0x12345678
0x78

0x12345678
0x78
0x56
0x34
0x12

0x12345699
0x12349999
0x12999999
0x99999999
*/

本次教學到這邊結束,另外,讀者也可以回顧我在指標那一章節所畫的圖示,觀察一下那張圖是哪一種位元組順序。

留言
avatar-img
電資鼠 - 您的學習好夥伴
19會員
242內容數
在當今數位時代,電資領域人才需求爆發式成長,不論是前端網頁設計、嵌入式開發、人工智慧、物聯網還是軟硬體整合,這些技術都在改變世界。而掌握 C/C++、Python、數位邏輯、電路學與嵌入式開發等大學電資領域的課程,正是進入這個高薪、高需求產業的關鍵!
2025/03/07
本章節將探討左下三角稀疏矩陣。
Thumbnail
2025/03/07
本章節將探討左下三角稀疏矩陣。
Thumbnail
2025/03/07
相信讀者現在對於鏈結串列有了更多的認識,所以我再進一步,示範更多關於鏈結串列的操作,這部分示範會將程式模組化。將鏈結串列的操作寫進一個標頭檔,並在主程式中引入。
Thumbnail
2025/03/07
相信讀者現在對於鏈結串列有了更多的認識,所以我再進一步,示範更多關於鏈結串列的操作,這部分示範會將程式模組化。將鏈結串列的操作寫進一個標頭檔,並在主程式中引入。
Thumbnail
2025/03/07
本章節示範透過「陣列索引」和「指標運算」兩種方式來存取同一個二維陣列 a,並印出相同的數值以及對應的位址,以說明它們其實指向的是同一塊連續的記憶體空間。本文將依序解釋各段程式碼,並示範可能的執行結果與背後原理。
Thumbnail
2025/03/07
本章節示範透過「陣列索引」和「指標運算」兩種方式來存取同一個二維陣列 a,並印出相同的數值以及對應的位址,以說明它們其實指向的是同一塊連續的記憶體空間。本文將依序解釋各段程式碼,並示範可能的執行結果與背後原理。
Thumbnail
看更多
你可能也想看
Thumbnail
在 vocus 與你一起探索內容、發掘靈感的路上,我們又將啟動新的冒險——vocus App 正式推出! 現在起,你可以在 iOS App Store 下載全新上架的 vocus App。 無論是在通勤路上、日常空檔,或一天結束後的放鬆時刻,都能自在沈浸在內容宇宙中。
Thumbnail
在 vocus 與你一起探索內容、發掘靈感的路上,我們又將啟動新的冒險——vocus App 正式推出! 現在起,你可以在 iOS App Store 下載全新上架的 vocus App。 無論是在通勤路上、日常空檔,或一天結束後的放鬆時刻,都能自在沈浸在內容宇宙中。
Thumbnail
vocus 慶祝推出 App,舉辦 2026 全站慶。推出精選內容與數位商品折扣,訂單免費與紅包抽獎、新註冊會員專屬活動、Boba Boost 贊助抽紅包,以及全站徵文,並邀請你一起來回顧過去的一年, vocus 與創作者共同留下了哪些精彩創作。
Thumbnail
vocus 慶祝推出 App,舉辦 2026 全站慶。推出精選內容與數位商品折扣,訂單免費與紅包抽獎、新註冊會員專屬活動、Boba Boost 贊助抽紅包,以及全站徵文,並邀請你一起來回顧過去的一年, vocus 與創作者共同留下了哪些精彩創作。
Thumbnail
本篇文章深入淺出地介紹 C 語言中的資料型別,包含 enum、struct、union 和 typedef 的使用方法與應用情境。並透過圖解說明 struct 的記憶體配置和 union 的特性,以及位元組順序 (Endianness) 的概念。文末並附帶練習題,幫助讀者理解相關知識。
Thumbnail
本篇文章深入淺出地介紹 C 語言中的資料型別,包含 enum、struct、union 和 typedef 的使用方法與應用情境。並透過圖解說明 struct 的記憶體配置和 union 的特性,以及位元組順序 (Endianness) 的概念。文末並附帶練習題,幫助讀者理解相關知識。
Thumbnail
一般在使用 TypeScript 的時候,大家都有遇過定義列舉資料的情境吧。 不過不管是 enum 和 literal 的方式其實都有些小缺點,以下推薦一個個人認為體驗更好的方式。
Thumbnail
一般在使用 TypeScript 的時候,大家都有遇過定義列舉資料的情境吧。 不過不管是 enum 和 literal 的方式其實都有些小缺點,以下推薦一個個人認為體驗更好的方式。
Thumbnail
本章節旨在介紹 TypeScript 的基本資料型別,包括內建型別、型別轉換、自訂型別、元組、集合、陣列、和字典型別。透過理解和使用這些型別,可以提高代碼的可讀性和可維護性。
Thumbnail
本章節旨在介紹 TypeScript 的基本資料型別,包括內建型別、型別轉換、自訂型別、元組、集合、陣列、和字典型別。透過理解和使用這些型別,可以提高代碼的可讀性和可維護性。
Thumbnail
本章節的目的是讓讀者瞭解C#的物件導向特性,包括類別、繼承、多型、封裝等基本概念,以及介面、抽象類別、靜態類別等進階主題。此外,本章節也將介紹如何使用列舉、委派、Lambda表達式、泛型及反射,這些都是C#中常見的強大功能。
Thumbnail
本章節的目的是讓讀者瞭解C#的物件導向特性,包括類別、繼承、多型、封裝等基本概念,以及介面、抽象類別、靜態類別等進階主題。此外,本章節也將介紹如何使用列舉、委派、Lambda表達式、泛型及反射,這些都是C#中常見的強大功能。
Thumbnail
內容涵蓋資料型別、型別轉換、自訂型別、元組型別、集合型別和字典型別等主題。文章首先詳述內建型別如bool、byte、char等的定義和使用,接著討論型別轉換,包括隱含轉換和明確轉換。之後文章介紹自訂型別的建立,以及元組、集合、陣列和字典型別的操作與例子。
Thumbnail
內容涵蓋資料型別、型別轉換、自訂型別、元組型別、集合型別和字典型別等主題。文章首先詳述內建型別如bool、byte、char等的定義和使用,接著討論型別轉換,包括隱含轉換和明確轉換。之後文章介紹自訂型別的建立,以及元組、集合、陣列和字典型別的操作與例子。
Thumbnail
C#程式由一或多個檔案組成,包含命名空間、類別、結構、介面、列舉和委派等型別。Main方法是C#應用程式的進入點。在C#中,註解用於在程式碼中添加說明,有單行和多行兩種類型。變數的定義需要指定變數的類型和名稱,可以一次為多個變數賦值。
Thumbnail
C#程式由一或多個檔案組成,包含命名空間、類別、結構、介面、列舉和委派等型別。Main方法是C#應用程式的進入點。在C#中,註解用於在程式碼中添加說明,有單行和多行兩種類型。變數的定義需要指定變數的類型和名稱,可以一次為多個變數賦值。
Thumbnail
C#是一種開源、跨平台、面向對象的編程語言,具有類型安全、泛型、模式匹配等特性。廣泛應用於桌面和Web應用程序、遊戲開發、移動應用、雲計算等領域。全球數十萬家公司像微軟、Unity Technologies、Stack Overflow等使用C#支持其業務。C#還提供豐富的進階學習資源和主題。
Thumbnail
C#是一種開源、跨平台、面向對象的編程語言,具有類型安全、泛型、模式匹配等特性。廣泛應用於桌面和Web應用程序、遊戲開發、移動應用、雲計算等領域。全球數十萬家公司像微軟、Unity Technologies、Stack Overflow等使用C#支持其業務。C#還提供豐富的進階學習資源和主題。
Thumbnail
完成了Debug.log()的測試,接著還是要跟各位簡單講一下C#的一些規則,之後看程式會(比較)看得懂。 又講到變數? 在Unity中,變數是重要的工具,用來儲存和管理資料。讓開發者能夠靈活調整遊戲的行為和性能,減少代碼的重複性,使得遊戲開發更加高效和簡潔。透過使用變數,開發者可以輕鬆修改資料
Thumbnail
完成了Debug.log()的測試,接著還是要跟各位簡單講一下C#的一些規則,之後看程式會(比較)看得懂。 又講到變數? 在Unity中,變數是重要的工具,用來儲存和管理資料。讓開發者能夠靈活調整遊戲的行為和性能,減少代碼的重複性,使得遊戲開發更加高效和簡潔。透過使用變數,開發者可以輕鬆修改資料
Thumbnail
介紹C++ 語法 資料型態,架構說明 程式語言為人類與電腦溝通的工具 程式設計流程: 定義問題 -> 問題分析 -> 撰寫演算法 ->程式撰寫 -> 程式執行及維護
Thumbnail
介紹C++ 語法 資料型態,架構說明 程式語言為人類與電腦溝通的工具 程式設計流程: 定義問題 -> 問題分析 -> 撰寫演算法 ->程式撰寫 -> 程式執行及維護
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News