學習目標
- • 了解 C 語言的歷史和特色
- • 掌握基本語法和資料型別
- • 學會指標和記憶體管理
- • 掌握陣列、字串和結構體
- • 學會函數和檔案處理
- • 能夠編寫系統層級程式
第1課:C 語言簡介與第一個程式
中級了解什麼是 C 語言,它的歷史和特色,並編寫你的第一個 C 程式。
什麼是 C 語言?
C 語言是由 Dennis Ritchie 在 1972 年於貝爾實驗室開發的程式語言。它是一種通用的、程序式的程式語言,被譽為「程式語言之母」。
C 語言的核心特色:
- 低階控制:可以直接操作記憶體和硬體
- 高效能:編譯後的程式執行速度非常快
- 可移植性:程式碼可以在不同平台上編譯執行
- 簡潔語法:語言核心功能精簡,易於學習
- 系統程式:作業系統和嵌入式系統開發的首選
- 影響深遠:許多現代語言都受到 C 語言影響
你的第一個 C 程式:
// hello.c
// 這是你的第一個 C 程式
#include // 引入標準輸入輸出函式庫
// main 函數是程式的進入點
int main() {
// 在螢幕上輸出文字
printf("Hello, World!\n");
printf("歡迎來到 C 語言的世界!\n");
printf("C 語言是系統程式設計的基礎\n");
// 宣告和使用變數
int year = 1972; // 整數
float version = 11.0; // 浮點數
char grade = 'A'; // 字元
// 使用格式化輸出
printf("C 語言發明年份:%d\n", year);
printf("目前版本:%.1f\n", version);
printf("重要性等級:%c\n", grade);
// 程式正常結束
return 0;
}
程式碼解釋:
- #include <stdio.h>:引入標準輸入輸出函式庫
- int main():主函數,程式從這裡開始執行
- printf():格式化輸出函數,用於顯示文字
- \n:換行字元
- %d, %f, %c:格式化輸出的佔位符
- return 0:表示程式正常結束
- // 註解:單行註解,編譯器會忽略
編譯和執行:
C 語言是編譯式語言,需要先編譯再執行:
1. 編寫程式碼:建立 .c 檔案
2. 編譯:使用編譯器(如 gcc)編譯成執行檔
3. 執行:執行編譯後的程式
# 使用 gcc 編譯器
gcc hello.c -o hello
# 執行程式
./hello
💡 實作練習
修改程式碼,讓它顯示你的姓名、年齡和興趣,體驗 C 語言變數和格式化輸出的使用!
C 語言的應用領域:
🖥️ 作業系統
Linux、Windows 核心
🔧 嵌入式系統
微控制器、IoT 設備
🗄️ 資料庫系統
MySQL、PostgreSQL
🎮 遊戲引擎
高效能遊戲開發
第2課:基本資料型別與變數
中級學習 C 語言的基本資料型別、變數宣告和使用方法。
基本資料型別:
#include
int main() {
// 整數型別
char smallNumber = 127; // 8位元,-128 到 127
short mediumNumber = 32767; // 16位元,-32,768 到 32,767
int number = 2147483647; // 32位元,最常用的整數型別
long bigNumber = 2147483647L; // 32位元或64位元,加 L 後綴
// 無符號整數型別
unsigned char uChar = 255; // 0 到 255
unsigned int uInt = 4294967295U; // 0 到 4,294,967,295
// 浮點數型別
float decimal1 = 3.14f; // 32位元,6-7位有效數字
double decimal2 = 3.14159265359; // 64位元,15-17位有效數字
// 字元型別
char letter = 'A'; // 單一字元
char newline = '\n'; // 特殊字元
// 輸出所有變數
printf("=== 整數型別 ===\n");
printf("char: %d (大小: %zu bytes)\n", smallNumber, sizeof(char));
printf("short: %d (大小: %zu bytes)\n", mediumNumber, sizeof(short));
printf("int: %d (大小: %zu bytes)\n", number, sizeof(int));
printf("long: %ld (大小: %zu bytes)\n", bigNumber, sizeof(long));
printf("\n=== 無符號整數 ===\n");
printf("unsigned char: %u\n", uChar);
printf("unsigned int: %u\n", uInt);
printf("\n=== 浮點數 ===\n");
printf("float: %.6f (大小: %zu bytes)\n", decimal1, sizeof(float));
printf("double: %.15f (大小: %zu bytes)\n", decimal2, sizeof(double));
printf("\n=== 字元 ===\n");
printf("字元: %c (ASCII: %d)\n", letter, letter);
return 0;
}
變數宣告和初始化:
#include
int main() {
// 變數宣告
int age; // 宣告但未初始化
float height; // 宣告但未初始化
// 變數初始化
age = 25; // 賦值
height = 175.5; // 賦值
// 宣告並初始化
int score = 95;
char grade = 'A';
double weight = 70.5;
// 常數宣告(使用 const 或 #define)
const double PI = 3.14159;
const int MAX_STUDENTS = 100;
// 多個變數同時宣告
int x = 10, y = 20, z = 30;
// 變數命名規則示例
int student_age = 20; // 使用底線
int studentAge = 20; // 駝峰命名法(較少用)
int _private_var = 5; // 可以用底線開頭
// 輸出變數值
printf("=== 個人資訊 ===\n");
printf("年齡: %d 歲\n", age);
printf("身高: %.1f 公分\n", height);
printf("體重: %.1f 公斤\n", weight);
printf("分數: %d 分\n", score);
printf("等級: %c\n", grade);
printf("\n=== 常數 ===\n");
printf("圓周率: %.5f\n", PI);
printf("最大學生數: %d\n", MAX_STUDENTS);
printf("\n=== 座標 ===\n");
printf("座標: (%d, %d, %d)\n", x, y, z);
// 計算 BMI
double bmi = weight / ((height/100) * (height/100));
printf("\nBMI: %.2f\n", bmi);
return 0;
}
格式化輸出:
#include
int main() {
int number = 42;
float pi = 3.14159;
char letter = 'C';
// 基本格式化輸出
printf("整數: %d\n", number);
printf("浮點數: %f\n", pi);
printf("字元: %c\n", letter);
// 進階格式化
printf("\n=== 數字格式化 ===\n");
printf("預設: %d\n", number);
printf("5位數字,右對齊: %5d\n", number);
printf("5位數字,左對齊: %-5d|\n", number);
printf("前面補零: %05d\n", number);
printf("\n=== 浮點數格式化 ===\n");
printf("預設: %f\n", pi);
printf("2位小數: %.2f\n", pi);
printf("6位小數: %.6f\n", pi);
printf("科學記號: %e\n", pi);
printf("較短格式: %g\n", pi);
printf("\n=== 十六進位和八進位 ===\n");
printf("十進位: %d\n", number);
printf("十六進位: %x\n", number);
printf("十六進位(大寫): %X\n", number);
printf("八進位: %o\n", number);
printf("\n=== 字元和字串 ===\n");
printf("字元: %c\n", letter);
printf("ASCII 值: %d\n", letter);
// 特殊字元
printf("\n=== 特殊字元 ===\n");
printf("換行符號: 第一行\\n第二行\n");
printf("製表符號: 欄位1\\t欄位2\n");
printf("反斜線: \\\\\n");
printf("雙引號: \"Hello\"\n");
printf("單引號: \'C\'\n");
return 0;
}
💡 實作練習
建立一個程式儲存學生的姓名(用字元陣列)、年齡、身高、體重,並計算 BMI!
第3課:陣列與字串
進階學習 C 語言的陣列操作和字串處理,掌握資料集合的管理方法。
一維陣列:
#include
int main() {
// 陣列宣告和初始化
int numbers[5]; // 宣告大小為 5 的整數陣列
int scores[5] = {85, 92, 78, 96, 88}; // 宣告並初始化
int grades[] = {90, 85, 88, 92}; // 自動推導大小(4個元素)
// 個別賦值
numbers[0] = 10;
numbers[1] = 20;
numbers[2] = 30;
numbers[3] = 40;
numbers[4] = 50;
printf("=== 陣列基本操作 ===\n");
printf("scores 陣列大小:%zu 個元素\n", sizeof(scores) / sizeof(scores[0]));
// 存取陣列元素
printf("第一個分數:%d\n", scores[0]);
printf("最後一個分數:%d\n", scores[4]);
// 遍歷陣列
printf("\n所有分數:");
for (int i = 0; i < 5; i++) {
printf("%d ", scores[i]);
}
printf("\n");
printf("\nnumbers 陣列:");
for (int i = 0; i < 5; i++) {
printf("%d ", numbers[i]);
}
printf("\n");
// 計算陣列統計
int sum = 0;
int max = scores[0];
int min = scores[0];
for (int i = 0; i < 5; i++) {
sum += scores[i];
if (scores[i] > max) max = scores[i];
if (scores[i] < min) min = scores[i];
}
double average = (double)sum / 5;
printf("\n=== 統計資訊 ===\n");
printf("總分:%d\n", sum);
printf("平均分:%.2f\n", average);
printf("最高分:%d\n", max);
printf("最低分:%d\n", min);
// 陣列排序(氣泡排序)
int temp_array[5];
// 複製陣列
for (int i = 0; i < 5; i++) {
temp_array[i] = scores[i];
}
// 氣泡排序
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4 - i; j++) {
if (temp_array[j] > temp_array[j + 1]) {
int temp = temp_array[j];
temp_array[j] = temp_array[j + 1];
temp_array[j + 1] = temp;
}
}
}
printf("\n排序後的分數:");
for (int i = 0; i < 5; i++) {
printf("%d ", temp_array[i]);
}
printf("\n");
// 搜尋元素
int target = 92;
int found = -1;
for (int i = 0; i < 5; i++) {
if (scores[i] == target) {
found = i;
break;
}
}
if (found != -1) {
printf("\n找到分數 %d 在索引 %d\n", target, found);
} else {
printf("\n未找到分數 %d\n", target);
}
return 0;
}
多維陣列:
#include
int main() {
// 二維陣列宣告和初始化
int matrix[3][4]; // 3行4列的矩陣
// 初始化二維陣列
int grades[3][4] = {
{85, 92, 78, 96}, // 第一個學生的成績
{88, 91, 85, 89}, // 第二個學生的成績
{92, 87, 94, 91} // 第三個學生的成績
};
// 學生和科目名稱
char students[3][20] = {"小明", "小華", "小美"};
char subjects[4][10] = {"數學", "英文", "物理", "化學"};
printf("=== 學生成績表 ===\n");
// 顯示表頭
printf("學生\\科目\t");
for (int j = 0; j < 4; j++) {
printf("%s\t", subjects[j]);
}
printf("平均\n");
// 顯示每個學生的成績
for (int i = 0; i < 3; i++) {
printf("%s\t\t", students[i]);
int sum = 0;
for (int j = 0; j < 4; j++) {
printf("%d\t", grades[i][j]);
sum += grades[i][j];
}
double average = (double)sum / 4;
printf("%.1f\n", average);
}
// 計算各科平均分
printf("\n各科平均分:\n");
for (int j = 0; j < 4; j++) {
int sum = 0;
for (int i = 0; i < 3; i++) {
sum += grades[i][j];
}
double average = (double)sum / 3;
printf("%s:%.1f\n", subjects[j], average);
}
// 矩陣操作示例
printf("\n=== 矩陣操作 ===\n");
// 初始化矩陣
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
matrix[i][j] = i * 4 + j + 1;
}
}
printf("3x4 矩陣:\n");
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
printf("%2d ", matrix[i][j]);
}
printf("\n");
}
// 矩陣轉置(需要不同大小的矩陣)
int transposed[4][3];
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
transposed[j][i] = matrix[i][j];
}
}
printf("\n轉置後的 4x3 矩陣:\n");
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 3; j++) {
printf("%2d ", transposed[i][j]);
}
printf("\n");
}
// 找出最大值和位置
int max_value = grades[0][0];
int max_student = 0, max_subject = 0;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
if (grades[i][j] > max_value) {
max_value = grades[i][j];
max_student = i;
max_subject = j;
}
}
}
printf("\n最高分:%d\n", max_value);
printf("學生:%s\n", students[max_student]);
printf("科目:%s\n", subjects[max_subject]);
return 0;
}
字串基礎:
#include
#include // 字串函數庫
int main() {
// 字串宣告和初始化
char str1[50] = "Hello"; // 字元陣列初始化
char str2[] = "World"; // 自動推導大小
char str3[50]; // 未初始化的字元陣列
char *str4 = "C Language"; // 字串指標(唯讀)
printf("=== 字串基本操作 ===\n");
printf("str1: %s\n", str1);
printf("str2: %s\n", str2);
printf("str4: %s\n", str4);
// 字串長度
printf("\nstr1 長度:%zu\n", strlen(str1));
printf("str2 長度:%zu\n", strlen(str2));
printf("str4 長度:%zu\n", strlen(str4));
// 字串複製
strcpy(str3, str1); // 將 str1 複製到 str3
printf("複製後 str3: %s\n", str3);
// 字串連接
strcat(str1, " "); // 在 str1 後面加上空格
strcat(str1, str2); // 在 str1 後面加上 str2
printf("連接後 str1: %s\n", str1);
// 字串比較
int cmp_result = strcmp(str3, "Hello");
if (cmp_result == 0) {
printf("str3 等於 \"Hello\"\n");
} else if (cmp_result < 0) {
printf("str3 小於 \"Hello\"\n");
} else {
printf("str3 大於 \"Hello\"\n");
}
// 字串搜尋
char *found = strstr(str1, "World");
if (found != NULL) {
printf("在 str1 中找到 \"World\"\n");
}
// 字元搜尋
char *char_pos = strchr(str1, 'W');
if (char_pos != NULL) {
printf("字元 'W' 在位置:%ld\n", char_pos - str1);
}
printf("\n=== 字串輸入輸出 ===\n");
char name[50];
char message[100];
printf("請輸入您的姓名:");
// scanf("%s", name); // 注意:scanf 遇到空格會停止
// 使用 fgets 更安全
fgets(name, sizeof(name), stdin);
// 移除 fgets 讀入的換行符號
name[strcspn(name, "\n")] = '\0';
printf("您好,%s!\n", name);
// 格式化字串
sprintf(message, "歡迎 %s 學習 C 語言!", name);
printf("%s\n", message);
printf("\n=== 字串處理函數 ===\n");
char text[] = "Hello, C Programming!";
printf("原始字串:%s\n", text);
// 轉換為大寫(手動實作)
char upper_text[50];
strcpy(upper_text, text);
for (int i = 0; upper_text[i] != '\0'; i++) {
if (upper_text[i] >= 'a' && upper_text[i] <= 'z') {
upper_text[i] = upper_text[i] - 'a' + 'A';
}
}
printf("大寫:%s\n", upper_text);
// 計算字元出現次數
char target_char = 'l';
int count = 0;
for (int i = 0; text[i] != '\0'; i++) {
if (text[i] == target_char) {
count++;
}
}
printf("字元 '%c' 出現 %d 次\n", target_char, count);
// 反轉字串
char reversed[50];
int len = strlen(text);
for (int i = 0; i < len; i++) {
reversed[i] = text[len - 1 - i];
}
reversed[len] = '\0';
printf("反轉字串:%s\n", reversed);
return 0;
}
字串陣列與進階操作:
#include
#include
#include // 字元處理函數
int main() {
// 字串陣列
char fruits[5][20] = {
"蘋果",
"香蕉",
"橘子",
"葡萄",
"草莓"
};
// 字串指標陣列
char *colors[] = {
"紅色",
"綠色",
"藍色",
"黃色",
"紫色"
};
printf("=== 字串陣列操作 ===\n");
// 顯示所有水果
printf("水果清單:\n");
for (int i = 0; i < 5; i++) {
printf("%d. %s\n", i + 1, fruits[i]);
}
printf("\n顏色清單:\n");
for (int i = 0; i < 5; i++) {
printf("%d. %s\n", i + 1, colors[i]);
}
// 字串排序(氣泡排序)
char temp_fruits[5][20];
// 複製陣列
for (int i = 0; i < 5; i++) {
strcpy(temp_fruits[i], fruits[i]);
}
// 排序
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4 - i; j++) {
if (strcmp(temp_fruits[j], temp_fruits[j + 1]) > 0) {
char temp[20];
strcpy(temp, temp_fruits[j]);
strcpy(temp_fruits[j], temp_fruits[j + 1]);
strcpy(temp_fruits[j + 1], temp);
}
}
}
printf("\n排序後的水果:\n");
for (int i = 0; i < 5; i++) {
printf("%d. %s\n", i + 1, temp_fruits[i]);
}
printf("\n=== 字串分析 ===\n");
char sentence[] = "Hello World! This is C Programming.";
printf("句子:%s\n", sentence);
// 統計字元類型
int letters = 0, digits = 0, spaces = 0, others = 0;
for (int i = 0; sentence[i] != '\0'; i++) {
if (isalpha(sentence[i])) {
letters++;
} else if (isdigit(sentence[i])) {
digits++;
} else if (isspace(sentence[i])) {
spaces++;
} else {
others++;
}
}
printf("字母:%d 個\n", letters);
printf("數字:%d 個\n", digits);
printf("空格:%d 個\n", spaces);
printf("其他:%d 個\n", others);
// 單字計數
char text_copy[100];
strcpy(text_copy, sentence);
int word_count = 0;
char *token = strtok(text_copy, " !.");
printf("\n單字列表:\n");
while (token != NULL) {
word_count++;
printf("%d. %s\n", word_count, token);
token = strtok(NULL, " !.");
}
printf("總共 %d 個單字\n", word_count);
printf("\n=== 字串處理工具 ===\n");
char input[] = " Hello, World! ";
printf("原始字串:'%s'\n", input);
// 去除前後空格(簡單實作)
char trimmed[100];
int start = 0, end = strlen(input) - 1;
// 找到第一個非空格字元
while (input[start] == ' ') {
start++;
}
// 找到最後一個非空格字元
while (end >= 0 && input[end] == ' ') {
end--;
}
// 複製去除空格的部分
int j = 0;
for (int i = start; i <= end; i++) {
trimmed[j++] = input[i];
}
trimmed[j] = '\0';
printf("去除空格:'%s'\n", trimmed);
// 替換字元
char replace_text[] = "Hello World";
printf("替換前:%s\n", replace_text);
for (int i = 0; replace_text[i] != '\0'; i++) {
if (replace_text[i] == 'l') {
replace_text[i] = 'L';
}
}
printf("替換後:%s\n", replace_text);
// 檢查回文
char palindrome[] = "madam";
int len = strlen(palindrome);
int is_palindrome = 1;
for (int i = 0; i < len / 2; i++) {
if (palindrome[i] != palindrome[len - 1 - i]) {
is_palindrome = 0;
break;
}
}
printf("\n'%s' %s回文\n", palindrome, is_palindrome ? "是" : "不是");
return 0;
}
💡 實作練習
建立一個學生管理系統,使用陣列儲存學生姓名和成績,實作新增、搜尋、排序和統計功能!
第4課:函數與指標
進階學習 C 語言的函數定義和指標操作,掌握程式模組化和記憶體管理的核心概念。
函數基礎:
#include
// 函數宣告(原型)
void greet(void);
int add(int a, int b);
double calculateArea(double radius);
void printArray(int arr[], int size);
int factorial(int n);
// 無參數無回傳值的函數
void greet(void) {
printf("歡迎學習 C 語言函數!\n");
}
// 有參數有回傳值的函數
int add(int a, int b) {
return a + b;
}
// 計算圓面積
double calculateArea(double radius) {
const double PI = 3.14159;
return PI * radius * radius;
}
// 陣列作為參數
void printArray(int arr[], int size) {
printf("陣列內容:");
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
// 遞迴函數
int factorial(int n) {
if (n <= 1) {
return 1;
}
return n * factorial(n - 1);
}
// 費波那契數列
int fibonacci(int n) {
if (n <= 1) {
return n;
}
return fibonacci(n - 1) + fibonacci(n - 2);
}
// 判斷質數
int isPrime(int num) {
if (num <= 1) return 0;
if (num <= 3) return 1;
if (num % 2 == 0 || num % 3 == 0) return 0;
for (int i = 5; i * i <= num; i += 6) {
if (num % i == 0 || num % (i + 2) == 0) {
return 0;
}
}
return 1;
}
// 找出陣列最大值
int findMax(int arr[], int size) {
int max = arr[0];
for (int i = 1; i < size; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
return max;
}
int main() {
printf("=== 函數基本使用 ===\n");
// 呼叫無參數函數
greet();
// 呼叫有參數函數
int sum = add(10, 20);
printf("10 + 20 = %d\n", sum);
// 計算圓面積
double area = calculateArea(5.0);
printf("半徑 5.0 的圓面積:%.2f\n", area);
// 陣列參數
int numbers[] = {1, 2, 3, 4, 5};
int size = sizeof(numbers) / sizeof(numbers[0]);
printArray(numbers, size);
printf("\n=== 遞迴函數 ===\n");
// 階乘計算
for (int i = 1; i <= 5; i++) {
printf("%d! = %d\n", i, factorial(i));
}
// 費波那契數列
printf("\n費波那契數列前10項:");
for (int i = 0; i < 10; i++) {
printf("%d ", fibonacci(i));
}
printf("\n");
printf("\n=== 實用函數 ===\n");
// 質數檢查
printf("1到20的質數:");
for (int i = 1; i <= 20; i++) {
if (isPrime(i)) {
printf("%d ", i);
}
}
printf("\n");
// 找最大值
int data[] = {23, 45, 12, 67, 34, 89, 56};
int data_size = sizeof(data) / sizeof(data[0]);
int max = findMax(data, data_size);
printf("陣列最大值:%d\n", max);
return 0;
}
指標基礎:
#include
int main() {
printf("=== 指標基本概念 ===\n");
// 變數和指標宣告
int number = 42;
int *ptr; // 宣告整數指標
ptr = &number; // 指標指向 number 的位址
printf("變數 number 的值:%d\n", number);
printf("變數 number 的位址:%p\n", (void*)&number);
printf("指標 ptr 的值(位址):%p\n", (void*)ptr);
printf("指標 ptr 指向的值:%d\n", *ptr);
// 透過指標修改值
*ptr = 100;
printf("\n透過指標修改後:\n");
printf("number 的值:%d\n", number);
printf("*ptr 的值:%d\n", *ptr);
printf("\n=== 指標與陣列 ===\n");
int arr[] = {10, 20, 30, 40, 50};
int *arr_ptr = arr; // 陣列名稱就是指向第一個元素的指標
printf("陣列元素(使用陣列索引):\n");
for (int i = 0; i < 5; i++) {
printf("arr[%d] = %d\n", i, arr[i]);
}
printf("\n陣列元素(使用指標):\n");
for (int i = 0; i < 5; i++) {
printf("*(arr_ptr + %d) = %d\n", i, *(arr_ptr + i));
}
printf("\n陣列元素(指標遞增):\n");
int *temp_ptr = arr;
for (int i = 0; i < 5; i++) {
printf("*temp_ptr = %d\n", *temp_ptr);
temp_ptr++; // 指標移動到下一個元素
}
printf("\n=== 指標算術 ===\n");
int values[] = {100, 200, 300, 400, 500};
int *p = values;
printf("初始位置:*p = %d\n", *p);
p++; // 移動到下一個元素
printf("p++ 後:*p = %d\n", *p);
p += 2; // 移動兩個位置
printf("p += 2 後:*p = %d\n", *p);
p--; // 向前移動一個位置
printf("p-- 後:*p = %d\n", *p);
// 指標相減
int *start = values;
int *end = &values[4];
printf("陣列長度:%ld\n", end - start + 1);
printf("\n=== 字串與指標 ===\n");
char str[] = "Hello";
char *str_ptr = str;
printf("字串:%s\n", str);
printf("使用指標遍歷字串:\n");
while (*str_ptr != '\0') {
printf("字元:%c (ASCII: %d)\n", *str_ptr, *str_ptr);
str_ptr++;
}
// 字串指標
char *message = "C Programming";
printf("\n字串指標:%s\n", message);
// 字串陣列與指標陣列
char *languages[] = {"C", "C++", "Java", "Python"};
printf("\n程式語言:\n");
for (int i = 0; i < 4; i++) {
printf("%d. %s\n", i + 1, languages[i]);
}
printf("\n=== 指標與函數 ===\n");
// 將在下一個程式碼區塊中示範
return 0;
}
指標與函數:
#include
// 傳值呼叫(Call by Value)
void swapByValue(int a, int b) {
printf("函數內交換前:a = %d, b = %d\n", a, b);
int temp = a;
a = b;
b = temp;
printf("函數內交換後:a = %d, b = %d\n", a, b);
}
// 傳址呼叫(Call by Reference)
void swapByReference(int *a, int *b) {
printf("函數內交換前:*a = %d, *b = %d\n", *a, *b);
int temp = *a;
*a = *b;
*b = temp;
printf("函數內交換後:*a = %d, *b = %d\n", *a, *b);
}
// 修改陣列元素
void modifyArray(int arr[], int size) {
printf("在函數中修改陣列:\n");
for (int i = 0; i < size; i++) {
arr[i] *= 2; // 每個元素乘以2
printf("arr[%d] = %d\n", i, arr[i]);
}
}
// 計算陣列總和
int sumArray(int *arr, int size) {
int sum = 0;
for (int i = 0; i < size; i++) {
sum += arr[i]; // 等同於 sum += *(arr + i)
}
return sum;
}
// 尋找陣列中的最大值和最小值
void findMinMax(int arr[], int size, int *min, int *max) {
*min = *max = arr[0];
for (int i = 1; i < size; i++) {
if (arr[i] < *min) {
*min = arr[i];
}
if (arr[i] > *max) {
*max = arr[i];
}
}
}
// 字串長度計算(使用指標)
int stringLength(char *str) {
int length = 0;
while (*str != '\0') {
length++;
str++;
}
return length;
}
// 字串複製(使用指標)
void stringCopy(char *dest, char *src) {
while (*src != '\0') {
*dest = *src;
dest++;
src++;
}
*dest = '\0'; // 加上字串結束符號
}
// 反轉陣列
void reverseArray(int arr[], int size) {
int *start = arr;
int *end = arr + size - 1;
while (start < end) {
// 交換元素
int temp = *start;
*start = *end;
*end = temp;
start++;
end--;
}
}
int main() {
printf("=== 傳值 vs 傳址 ===\n");
int x = 10, y = 20;
printf("原始值:x = %d, y = %d\n", x, y);
// 傳值呼叫
printf("\n--- 傳值呼叫 ---\n");
swapByValue(x, y);
printf("函數呼叫後:x = %d, y = %d\n", x, y);
// 傳址呼叫
printf("\n--- 傳址呼叫 ---\n");
swapByReference(&x, &y);
printf("函數呼叫後:x = %d, y = %d\n", x, y);
printf("\n=== 陣列與指標函數 ===\n");
int numbers[] = {1, 2, 3, 4, 5};
int size = sizeof(numbers) / sizeof(numbers[0]);
printf("原始陣列:");
for (int i = 0; i < size; i++) {
printf("%d ", numbers[i]);
}
printf("\n");
// 計算總和
int total = sumArray(numbers, size);
printf("陣列總和:%d\n", total);
// 修改陣列
modifyArray(numbers, size);
printf("修改後陣列:");
for (int i = 0; i < size; i++) {
printf("%d ", numbers[i]);
}
printf("\n");
// 尋找最大最小值
int min, max;
findMinMax(numbers, size, &min, &max);
printf("最小值:%d,最大值:%d\n", min, max);
printf("\n=== 字串指標函數 ===\n");
char original[] = "Hello World";
char copy[50];
printf("原始字串:%s\n", original);
printf("字串長度:%d\n", stringLength(original));
stringCopy(copy, original);
printf("複製字串:%s\n", copy);
printf("\n=== 陣列反轉 ===\n");
int data[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int data_size = sizeof(data) / sizeof(data[0]);
printf("反轉前:");
for (int i = 0; i < data_size; i++) {
printf("%d ", data[i]);
}
printf("\n");
reverseArray(data, data_size);
printf("反轉後:");
for (int i = 0; i < data_size; i++) {
printf("%d ", data[i]);
}
printf("\n");
printf("\n=== 指標的指標 ===\n");
int value = 100;
int *ptr1 = &value;
int **ptr2 = &ptr1; // 指向指標的指標
printf("value = %d\n", value);
printf("*ptr1 = %d\n", *ptr1);
printf("**ptr2 = %d\n", **ptr2);
printf("value 的位址:%p\n", (void*)&value);
printf("ptr1 的值:%p\n", (void*)ptr1);
printf("ptr1 的位址:%p\n", (void*)&ptr1);
printf("ptr2 的值:%p\n", (void*)ptr2);
// 透過二級指標修改值
**ptr2 = 200;
printf("透過 **ptr2 修改後,value = %d\n", value);
return 0;
}
💡 實作練習
建立一個動態陣列管理系統,使用指標實作陣列的新增、刪除、搜尋和排序功能!
第5課:結構體與檔案處理
進階學習 C 語言的結構體定義和檔案操作,掌握複雜資料結構和資料持久化技術。
結構體基礎:
#include
#include
// 定義學生結構體
struct Student {
int id;
char name[50];
int age;
float gpa;
char major[30];
};
// 定義座標點結構體
struct Point {
double x;
double y;
};
// 定義日期結構體
struct Date {
int day;
int month;
int year;
};
// 定義員工結構體(包含日期結構體)
struct Employee {
int id;
char name[50];
struct Date birthDate;
struct Date hireDate;
double salary;
};
// 使用 typedef 簡化結構體名稱
typedef struct {
char title[100];
char author[50];
int pages;
double price;
} Book;
// 函數:顯示學生資訊
void displayStudent(struct Student s) {
printf("=== 學生資訊 ===\n");
printf("學號:%d\n", s.id);
printf("姓名:%s\n", s.name);
printf("年齡:%d\n", s.age);
printf("GPA:%.2f\n", s.gpa);
printf("主修:%s\n", s.major);
}
// 函數:計算兩點距離
double calculateDistance(struct Point p1, struct Point p2) {
double dx = p2.x - p1.x;
double dy = p2.y - p1.y;
return sqrt(dx * dx + dy * dy);
}
// 函數:顯示日期
void displayDate(struct Date date) {
printf("%04d/%02d/%02d", date.year, date.month, date.day);
}
int main() {
printf("=== 結構體基本使用 ===\n");
// 結構體變數宣告和初始化
struct Student student1;
// 個別賦值
student1.id = 1001;
strcpy(student1.name, "張小明");
student1.age = 20;
student1.gpa = 3.75;
strcpy(student1.major, "資訊工程");
// 初始化時賦值
struct Student student2 = {1002, "李小華", 21, 3.85, "電機工程"};
// 顯示學生資訊
displayStudent(student1);
printf("\n");
displayStudent(student2);
printf("\n=== 結構體陣列 ===\n");
// 結構體陣列
struct Student class[3] = {
{1001, "張小明", 20, 3.75, "資訊工程"},
{1002, "李小華", 21, 3.85, "電機工程"},
{1003, "王小美", 19, 3.92, "數學系"}
};
printf("班級學生名單:\n");
for (int i = 0; i < 3; i++) {
printf("%d. %s (ID: %d, GPA: %.2f)\n",
i + 1, class[i].name, class[i].id, class[i].gpa);
}
// 計算班級平均 GPA
double totalGPA = 0;
for (int i = 0; i < 3; i++) {
totalGPA += class[i].gpa;
}
double averageGPA = totalGPA / 3;
printf("班級平均 GPA:%.2f\n", averageGPA);
printf("\n=== 座標點計算 ===\n");
struct Point p1 = {0.0, 0.0};
struct Point p2 = {3.0, 4.0};
printf("點 1:(%.1f, %.1f)\n", p1.x, p1.y);
printf("點 2:(%.1f, %.1f)\n", p2.x, p2.y);
double distance = calculateDistance(p1, p2);
printf("兩點距離:%.2f\n", distance);
printf("\n=== 巢狀結構體 ===\n");
struct Employee emp = {
101,
"陳大明",
{15, 3, 1985}, // 生日
{1, 6, 2020}, // 到職日
55000.0
};
printf("員工資訊:\n");
printf("員工編號:%d\n", emp.id);
printf("姓名:%s\n", emp.name);
printf("生日:");
displayDate(emp.birthDate);
printf("\n到職日:");
displayDate(emp.hireDate);
printf("\n薪水:%.2f\n", emp.salary);
printf("\n=== typedef 結構體 ===\n");
Book book1 = {"C 程式設計", "Dennis Ritchie", 272, 450.0};
Book book2;
strcpy(book2.title, "資料結構與演算法");
strcpy(book2.author, "Thomas Cormen");
book2.pages = 1312;
book2.price = 1200.0;
printf("書籍 1:\n");
printf(" 書名:%s\n", book1.title);
printf(" 作者:%s\n", book1.author);
printf(" 頁數:%d\n", book1.pages);
printf(" 價格:%.2f\n", book1.price);
printf("\n書籍 2:\n");
printf(" 書名:%s\n", book2.title);
printf(" 作者:%s\n", book2.author);
printf(" 頁數:%d\n", book2.pages);
printf(" 價格:%.2f\n", book2.price);
printf("\n=== 結構體大小 ===\n");
printf("Student 結構體大小:%zu bytes\n", sizeof(struct Student));
printf("Point 結構體大小:%zu bytes\n", sizeof(struct Point));
printf("Date 結構體大小:%zu bytes\n", sizeof(struct Date));
printf("Employee 結構體大小:%zu bytes\n", sizeof(struct Employee));
printf("Book 結構體大小:%zu bytes\n", sizeof(Book));
return 0;
}
結構體與指標:
#include
#include
#include
// 定義鏈結串列節點
struct Node {
int data;
struct Node* next;
};
// 定義學生結構體
typedef struct {
int id;
char name[50];
float gpa;
} Student;
// 函數:透過指標修改學生資訊
void updateStudent(Student* s, int newId, const char* newName, float newGpa) {
s->id = newId; // 等同於 (*s).id = newId
strcpy(s->name, newName);
s->gpa = newGpa;
}
// 函數:顯示學生資訊(使用指標)
void printStudent(const Student* s) {
printf("學號:%d, 姓名:%s, GPA:%.2f\n", s->id, s->name, s->gpa);
}
// 函數:比較兩個學生的 GPA
int compareGPA(const Student* s1, const Student* s2) {
if (s1->gpa > s2->gpa) return 1;
if (s1->gpa < s2->gpa) return -1;
return 0;
}
// 函數:在鏈結串列開頭插入節點
struct Node* insertAtBeginning(struct Node* head, int value) {
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
if (newNode == NULL) {
printf("記憶體配置失敗\n");
return head;
}
newNode->data = value;
newNode->next = head;
return newNode;
}
// 函數:顯示鏈結串列
void printList(struct Node* head) {
struct Node* current = head;
printf("鏈結串列:");
while (current != NULL) {
printf("%d ", current->data);
current = current->next;
}
printf("\n");
}
// 函數:釋放鏈結串列記憶體
void freeList(struct Node* head) {
struct Node* current = head;
struct Node* next;
while (current != NULL) {
next = current->next;
free(current);
current = next;
}
}
// 函數:動態配置學生陣列
Student* createStudentArray(int size) {
Student* students = (Student*)malloc(size * sizeof(Student));
if (students == NULL) {
printf("記憶體配置失敗\n");
return NULL;
}
return students;
}
int main() {
printf("=== 結構體指標基礎 ===\n");
Student student = {1001, "張小明", 3.75};
Student* ptr = &student;
printf("使用變數存取:\n");
printf("學號:%d, 姓名:%s, GPA:%.2f\n",
student.id, student.name, student.gpa);
printf("\n使用指標存取:\n");
printf("學號:%d, 姓名:%s, GPA:%.2f\n",
ptr->id, ptr->name, ptr->gpa);
// 透過指標修改
updateStudent(ptr, 1002, "李小華", 3.85);
printf("\n修改後:\n");
printStudent(ptr);
printf("\n=== 結構體陣列與指標 ===\n");
// 動態配置學生陣列
int numStudents = 3;
Student* students = createStudentArray(numStudents);
if (students != NULL) {
// 初始化學生資料
updateStudent(&students[0], 1001, "王小明", 3.75);
updateStudent(&students[1], 1002, "李小華", 3.85);
updateStudent(&students[2], 1003, "張小美", 3.92);
printf("學生清單:\n");
for (int i = 0; i < numStudents; i++) {
printf("%d. ", i + 1);
printStudent(&students[i]);
}
// 找出 GPA 最高的學生
Student* topStudent = &students[0];
for (int i = 1; i < numStudents; i++) {
if (compareGPA(&students[i], topStudent) > 0) {
topStudent = &students[i];
}
}
printf("\nGPA 最高的學生:\n");
printStudent(topStudent);
// 釋放記憶體
free(students);
}
printf("\n=== 鏈結串列示範 ===\n");
struct Node* head = NULL;
// 插入節點
head = insertAtBeginning(head, 10);
head = insertAtBeginning(head, 20);
head = insertAtBeginning(head, 30);
head = insertAtBeginning(head, 40);
printList(head);
// 遍歷並修改節點
struct Node* current = head;
printf("將所有值乘以 2:\n");
while (current != NULL) {
current->data *= 2;
current = current->next;
}
printList(head);
// 計算節點數量
current = head;
int count = 0;
while (current != NULL) {
count++;
current = current->next;
}
printf("節點數量:%d\n", count);
// 釋放鏈結串列記憶體
freeList(head);
printf("\n=== 結構體函數指標 ===\n");
// 定義包含函數指標的結構體
typedef struct {
int (*add)(int, int);
int (*multiply)(int, int);
} Calculator;
// 定義計算函數
int add(int a, int b) { return a + b; }
int multiply(int a, int b) { return a * b; }
// 建立計算器
Calculator calc = {add, multiply};
printf("5 + 3 = %d\n", calc.add(5, 3));
printf("5 * 3 = %d\n", calc.multiply(5, 3));
return 0;
}
檔案處理基礎:
#include
#include
#include
// 學生結構體
typedef struct {
int id;
char name[50];
float gpa;
} Student;
// 函數:寫入文字檔案
void writeTextFile() {
FILE *file = fopen("students.txt", "w");
if (file == NULL) {
printf("無法開啟檔案進行寫入\n");
return;
}
fprintf(file, "學生資料\n");
fprintf(file, "========\n");
fprintf(file, "1001,張小明,3.75\n");
fprintf(file, "1002,李小華,3.85\n");
fprintf(file, "1003,王小美,3.92\n");
fclose(file);
printf("文字檔案寫入完成:students.txt\n");
}
// 函數:讀取文字檔案
void readTextFile() {
FILE *file = fopen("students.txt", "r");
if (file == NULL) {
printf("無法開啟檔案進行讀取\n");
return;
}
char line[100];
printf("\n讀取文字檔案內容:\n");
while (fgets(line, sizeof(line), file) != NULL) {
printf("%s", line);
}
fclose(file);
}
// 函數:寫入二進位檔案
void writeBinaryFile() {
FILE *file = fopen("students.dat", "wb");
if (file == NULL) {
printf("無法開啟二進位檔案進行寫入\n");
return;
}
Student students[] = {
{1001, "張小明", 3.75},
{1002, "李小華", 3.85},
{1003, "王小美", 3.92}
};
int count = sizeof(students) / sizeof(students[0]);
// 寫入學生數量
fwrite(&count, sizeof(int), 1, file);
// 寫入學生資料
fwrite(students, sizeof(Student), count, file);
fclose(file);
printf("二進位檔案寫入完成:students.dat\n");
}
// 函數:讀取二進位檔案
void readBinaryFile() {
FILE *file = fopen("students.dat", "rb");
if (file == NULL) {
printf("無法開啟二進位檔案進行讀取\n");
return;
}
int count;
// 讀取學生數量
if (fread(&count, sizeof(int), 1, file) != 1) {
printf("讀取學生數量失敗\n");
fclose(file);
return;
}
printf("\n從二進位檔案讀取 %d 個學生:\n", count);
// 讀取學生資料
Student student;
for (int i = 0; i < count; i++) {
if (fread(&student, sizeof(Student), 1, file) == 1) {
printf("學號:%d, 姓名:%s, GPA:%.2f\n",
student.id, student.name, student.gpa);
}
}
fclose(file);
}
// 函數:追加資料到檔案
void appendToFile() {
FILE *file = fopen("students.txt", "a");
if (file == NULL) {
printf("無法開啟檔案進行追加\n");
return;
}
fprintf(file, "1004,陳小強,3.68\n");
fprintf(file, "1005,林小雅,3.91\n");
fclose(file);
printf("資料追加完成\n");
}
// 函數:檔案複製
void copyFile(const char* source, const char* destination) {
FILE *src = fopen(source, "r");
if (src == NULL) {
printf("無法開啟來源檔案:%s\n", source);
return;
}
FILE *dest = fopen(destination, "w");
if (dest == NULL) {
printf("無法建立目標檔案:%s\n", destination);
fclose(src);
return;
}
char ch;
while ((ch = fgetc(src)) != EOF) {
fputc(ch, dest);
}
fclose(src);
fclose(dest);
printf("檔案複製完成:%s -> %s\n", source, destination);
}
// 函數:統計檔案資訊
void analyzeFile(const char* filename) {
FILE *file = fopen(filename, "r");
if (file == NULL) {
printf("無法開啟檔案:%s\n", filename);
return;
}
int lines = 0, words = 0, chars = 0;
char ch, prev_ch = ' ';
while ((ch = fgetc(file)) != EOF) {
chars++;
if (ch == '\n') {
lines++;
}
if ((ch == ' ' || ch == '\n' || ch == '\t') &&
(prev_ch != ' ' && prev_ch != '\n' && prev_ch != '\t')) {
words++;
}
prev_ch = ch;
}
// 如果檔案不是以換行結尾,最後一行也要計算
if (chars > 0 && prev_ch != '\n') {
lines++;
}
// 如果最後一個字元不是空白,最後一個單字也要計算
if (chars > 0 && prev_ch != ' ' && prev_ch != '\n' && prev_ch != '\t') {
words++;
}
fclose(file);
printf("\n檔案統計:%s\n", filename);
printf("行數:%d\n", lines);
printf("單字數:%d\n", words);
printf("字元數:%d\n", chars);
}
// 函數:搜尋檔案內容
void searchInFile(const char* filename, const char* searchTerm) {
FILE *file = fopen(filename, "r");
if (file == NULL) {
printf("無法開啟檔案:%s\n", filename);
return;
}
char line[200];
int lineNumber = 1;
int found = 0;
printf("\n在檔案 %s 中搜尋 \"%s\":\n", filename, searchTerm);
while (fgets(line, sizeof(line), file) != NULL) {
if (strstr(line, searchTerm) != NULL) {
printf("第 %d 行:%s", lineNumber, line);
found = 1;
}
lineNumber++;
}
if (!found) {
printf("未找到 \"%s\"\n", searchTerm);
}
fclose(file);
}
int main() {
printf("=== 檔案處理示範 ===\n");
// 寫入和讀取文字檔案
writeTextFile();
readTextFile();
printf("\n=== 追加資料 ===\n");
appendToFile();
readTextFile();
printf("\n=== 二進位檔案操作 ===\n");
writeBinaryFile();
readBinaryFile();
printf("\n=== 檔案複製 ===\n");
copyFile("students.txt", "students_backup.txt");
printf("\n=== 檔案分析 ===\n");
analyzeFile("students.txt");
printf("\n=== 檔案搜尋 ===\n");
searchInFile("students.txt", "張小明");
searchInFile("students.txt", "3.9");
printf("\n=== 檔案位置操作 ===\n");
FILE *file = fopen("students.dat", "rb");
if (file != NULL) {
// 移動到檔案開頭
fseek(file, 0, SEEK_SET);
int count;
fread(&count, sizeof(int), 1, file);
printf("學生總數:%d\n", count);
// 移動到第二個學生記錄
fseek(file, sizeof(int) + sizeof(Student), SEEK_SET);
Student student;
if (fread(&student, sizeof(Student), 1, file) == 1) {
printf("第二個學生:%s\n", student.name);
}
// 取得目前檔案位置
long position = ftell(file);
printf("目前檔案位置:%ld\n", position);
fclose(file);
}
return 0;
}
💡 實作練習
建立一個完整的學生管理系統,使用結構體儲存學生資料,並實作檔案的讀取、寫入、搜尋和統計功能!