典例題100例:從基礎(chǔ)語法到鏈表文件讀寫的刷題路線)
簡介這是一份C語言經(jīng)典例題100例的PDF文檔面向C語言初學(xué)者和備考復(fù)習(xí)者系統(tǒng)覆蓋基本數(shù)據(jù)類型、控制結(jié)構(gòu)、函數(shù)、數(shù)組、指針、結(jié)構(gòu)體、文件操作等核心知識(shí)點(diǎn)通過100道經(jīng)典實(shí)例幫助讀者鞏固語法、理解編程邏輯。每個(gè)實(shí)例均包含題目描述、程序分析和完整源代碼。例如程序1用三重循環(huán)解決數(shù)字排列問題程序2用多分支if處理利潤提成程序3用sqrt判斷完全平方數(shù)程序4用switch和閏年判斷計(jì)算某天是第幾天這些題目能有效訓(xùn)練循環(huán)嵌套、條件分支、數(shù)學(xué)函數(shù)及邏輯思維。文檔共1個(gè)PDF文件壓縮包大小2.77MB內(nèi)容精煉便攜方便隨時(shí)查閱。目前已有1201人學(xué)習(xí)下載適合C語言小白自學(xué)、教師課堂參考及課后復(fù)習(xí)拓展。通過對(duì)照源代碼與詳細(xì)解釋讀者可以深入理解每一步計(jì)算過程舉一反三解決類似編程問題是入門C語言不可多得的練習(xí)合集。1. 為什么說“C語言經(jīng)典例題100例”是一份可以反復(fù)刷的路線圖手頭那份《C語言經(jīng)典例題100例.pdf》是不是已經(jīng)躺在下載目錄里吃灰很多人的學(xué)習(xí)路徑是看一遍C語言基礎(chǔ)知識(shí)的書跟著敲幾個(gè)例子然后就開始背代碼。結(jié)果到了面試或?qū)懬度胧巾?xiàng)目時(shí)鏈表、指針、文件讀寫組合在一起就露怯。這套經(jīng)典題的價(jià)值不在于“100”這個(gè)數(shù)字而在于它把分散的語法點(diǎn)擰成了一個(gè)個(gè)必須動(dòng)手解決的問題從輸入輸出、循環(huán)分支到數(shù)組與字符串函數(shù)再到結(jié)構(gòu)體、鏈表和文件操作。哪怕現(xiàn)在Python很火但計(jì)算機(jī)第一門專業(yè)課仍然從C講起因?yàn)橹羔樅蛢?nèi)存模型是后面所有系統(tǒng)級(jí)編程的地基。這份題適合兩類人一類是剛學(xué)完C語言基礎(chǔ)但不知道下一步做什么的入門者另一類是已經(jīng)工作數(shù)年、需要快速恢復(fù)對(duì)底層語感的后端或嵌入式工程師。我建議不要從頭到尾按順序刷而是先按知識(shí)塊把題目拆開再用編譯、調(diào)試工具把每道題跑通。后面的章節(jié)會(huì)給你一套可復(fù)現(xiàn)的操作路徑。2. 先把100例拆成6個(gè)知識(shí)塊再按塊補(bǔ)齊C語言基礎(chǔ)與指針2.1 用一張歸類表制定刷題順序避免淹沒在題海里打開PDF后先別急著看第一題。一般情況下這100例并不是按難度嚴(yán)格排隊(duì)的但題目之間有明顯的高頻考點(diǎn)。常見做法是把它們按考查對(duì)象分塊每一塊集中刷比按頁碼順序刷效率高很多。我習(xí)慣分成6塊知識(shí)塊覆蓋的典型題目方向刷完后的驗(yàn)證標(biāo)準(zhǔn)基礎(chǔ)語法與輸入輸出整數(shù)求和、判斷閏年、溫度轉(zhuǎn)換能不看參考寫出 scanf/printf 格式控制流程控制與循環(huán)九九乘法表、素?cái)?shù)判斷、水仙花數(shù)能寫出 while 和 do-while 等價(jià)的場景數(shù)組與字符串冒泡排序、字符串逆序、字符統(tǒng)計(jì)能說清數(shù)組名做參數(shù)時(shí)發(fā)生了什么函數(shù)與遞歸階乘、斐波那契、漢諾塔能手動(dòng)描出遞歸棧的變化指針、結(jié)構(gòu)體與鏈表指針交換、鏈表創(chuàng)建、學(xué)生成績排序能改自寫代碼而不崩潰文件與內(nèi)存管理文件復(fù)制、成績單存取、動(dòng)態(tài)內(nèi)存申請(qǐng)能跑通寫入和讀取并檢查錯(cuò)誤這張表很重要。你可以先把PDF里每道題的標(biāo)題或者第一行注釋掃描一遍給題目打上這樣的標(biāo)簽。哪怕只是用數(shù)字在頁邊標(biāo)上“數(shù)組”“指針”“文件”幾個(gè)字也比盲目刷三道相似的判斷素?cái)?shù)題強(qiáng)。順便說一句網(wǎng)上流傳的“C語言必背100代碼”很多就是把這類經(jīng)典題壓縮成模板但背著寫和能自己推導(dǎo)出邊界條件是兩回事。字符串?dāng)?shù)組作為參數(shù)這類題目如果沒歸到“數(shù)組與字符串”這一塊很容易在后面的鏈表題里被遺忘。2.2 指針題先分清“改變?cè)怠焙汀案淖冎羔樦赶颉?00例里但凡涉及到指針的題翻來覆去也就那幾個(gè)模型。最容易卡住新手的是為什么swap用int a,int b不能交換用int *a,int *b才能交換先看一段最小示例#include stdio.h void swap_wrong(int a, int b) { int t a; a b; b t; } void swap_right(int *a, int *b) { int t *a; *a *b; *b t; } int main() { int x 3, y 5; swap_wrong(x, y); printf(wrong: %d %d\n, x, y); swap_right(x, y); printf(right: %d %d\n, x, y); return 0; }swap_right 的參數(shù)接收的是 x 和 y 的地址函數(shù)內(nèi)部通過*a、*b間接修改了 main 函數(shù)棧上的變量。而 swap_wrong 只交換了形參副本調(diào)用結(jié)束后副本生命周期結(jié)束主調(diào)方看不到任何變化。所以指針在函數(shù)參數(shù)里的意義是“把地址傳進(jìn)去讓被調(diào)函數(shù)能改外部變量”。把這個(gè)模型記住后續(xù)讀鏈表題里的struct Student **head就不會(huì)發(fā)暈需要在函數(shù)里改變 head 指針本身時(shí)就要傳入指針的指針。2.3 數(shù)組參數(shù)會(huì)退化成指針這是很多運(yùn)行時(shí)問題的根源和指針綁定的另一個(gè)經(jīng)典誤區(qū)是“數(shù)組到底是不是指針”。在 main 里寫sizeof(arr) / sizeof(arr[0])能得到元素個(gè)數(shù)但一旦把數(shù)組作為參數(shù)傳給函數(shù)函數(shù)里的sizeof(arr)就成了指針大小通常是8字節(jié)于是數(shù)組長度就算錯(cuò)了。經(jīng)典例題里經(jīng)常要求自己實(shí)現(xiàn)一個(gè)排序函數(shù)很多人直接在函數(shù)里用sizeof算長度結(jié)果排序條件循環(huán)不執(zhí)行。void show_size(int arr[]) { printf(in function: %zu\n, sizeof(arr)); // 指針大小不是數(shù)組大小 } int main() { int arr[4] {1, 2, 3, 4}; printf(in main: %zu\n, sizeof(arr)); // 16byte show_size(arr); return 0; }解決辦法是函數(shù)參數(shù)里同時(shí)傳入長度或者用int *arr并搭配長度參數(shù)。100例里凡是要求“輸入n個(gè)整數(shù)再處理”的題目幾乎都會(huì)用到這個(gè)約定數(shù)組名當(dāng)實(shí)參傳的時(shí)候?qū)崊⒈緛砭椭皇鞘自氐刂匪员徽{(diào)函數(shù)拿不到數(shù)組邊界。記住這條再寫C語言鏈表或字符串?dāng)?shù)組作為參數(shù)時(shí)就少踩很多坑。3. 在本地跑通經(jīng)典題從冒泡排序到字符串函數(shù)看最小可編譯流程3.1 用 gcc 一行命令跑通最小程序參數(shù)含義要清楚很多人在PDF上看得懂代碼卻不知道怎么在本地運(yùn)行。最省事的方式是用gcc編譯單個(gè)源文件。假如題目要求實(shí)現(xiàn)冒泡排序則先建一個(gè)bubble.c然后把代碼貼進(jìn)去在終端執(zhí)行g(shù)cc -Wall -stdc11 -g -o bubble bubble.c ./bubble這里的-Wall是讓編譯器輸出常見警告能幫你發(fā)現(xiàn)變量未使用或類型不匹配刷題時(shí)建議始終打開-stdc11是指定C語言標(biāo)準(zhǔn)避免某些老舊教材寫法在新版gcc里出現(xiàn)兼容性問題-g是生成調(diào)試符號(hào)后面要用gdb單步驗(yàn)證時(shí)就離不開它-o bubble指定輸出文件名不寫的話默認(rèn)是a.out。如果是在Windows上的VSCode里配置C語言環(huán)境常見做法也是把gcc作為任務(wù)編譯器tasks.json里的command填同一個(gè)路徑原理完全一樣。3.2 冒泡排序C語言實(shí)現(xiàn)n-1-i這個(gè)邊界是怎么來的排序題幾乎每個(gè)版本都會(huì)出現(xiàn)。給出一版帶提前終止的寫法#include stdio.h void bubble_sort(int arr[], int n) { for (int i 0; i n - 1; i) { int swapped 0; for (int j 0; j n - 1 - i; j) { if (arr[j] arr[j 1]) { int t arr[j]; arr[j] arr[j 1]; arr[j 1] t; swapped 1; } } if (!swapped) break; } } int main() { int arr[] {54, 23, 12, 77, 9}; int n sizeof(arr) / sizeof(arr[0]); bubble_sort(arr, n); for (int i 0; i n; i) { printf(%d%c, arr[i], i n - 1 ? \n : ); } return 0; }外層第 i 輪結(jié)束后最大的 i1 個(gè)元素已經(jīng)沉到末尾所以內(nèi)層只需要比較到n - 1 - i這是“減少無用比較”的關(guān)鍵。swapped用來記錄這一輪是否發(fā)生過交換如果整個(gè)序列在一次遍歷后已經(jīng)有序就直接結(jié)束。參數(shù)arr[]雖然寫法像個(gè)數(shù)組實(shí)際傳進(jìn)來的仍是首地址函數(shù)內(nèi)修改會(huì)影響到main里的原數(shù)組。main 里sizeof(arr) / sizeof(arr[0])必須在數(shù)組定義所在的函數(shù)里計(jì)算一旦進(jìn)入bubble_sort就失效這也呼應(yīng)了上一章的退化問題。如果題目要求輸入動(dòng)態(tài)數(shù)量的數(shù)字可以把數(shù)組換成C語言long long數(shù)組注意讀取時(shí)用%lld排序邏輯不變。3.3 字符串函數(shù)那幾道題strcpy、strstr、字符串逆序一起練字符串在經(jīng)典例題里出現(xiàn)頻率極高。常見的題目有手寫strcpy、查找子串、逆序輸出。先看一個(gè)最精簡的字符串逆序?qū)崿F(xiàn)#include stdio.h #include string.h void reverse_str(char s[]) { int left 0; int right strlen(s) - 1; while (left right) { char t s[left]; s[left] s[right]; s[right] t; left; right--; } } int main() { char text[] hello; reverse_str(text); printf(%s\n, text); return 0; }strlen(s)返回的是不含\0的長度所以右邊界是len - 1。雙指針從兩端向中間移動(dòng)交換字符直到相遇。這里有個(gè)容易忽略的點(diǎn)如果定義成char *text hello字符串字面量會(huì)被放在只讀區(qū)在reverse_str里寫它會(huì)導(dǎo)致段錯(cuò)誤必須用字符數(shù)組char text[]才能原地修改。C語言字符串函數(shù)里還有strcpy(dest, src)不檢查目標(biāo)空間大小的坑以及strstr可以查找普通字符串但要注意它是按字節(jié)匹配的如果需要查找的片段里可能含\0就不適合。刷字符串逆序和子串查找題時(shí)最好順手把這些邊界條件都驗(yàn)證一遍。4. 進(jìn)階題必練鏈表、結(jié)構(gòu)體與文件讀寫把C語言常用語法串起來4.1 結(jié)構(gòu)體、typedef與自引用指針題題都能用文件讀寫題和鏈表題往往共用同一個(gè)數(shù)據(jù)結(jié)構(gòu)某個(gè)實(shí)體比如學(xué)生、圖書、商品有若干個(gè)字段還需要一個(gè)指向下一條記錄的指針。以最常見的“學(xué)生信息管理”為例結(jié)構(gòu)體一般這樣聲明#include stdio.h #include stdlib.h #include string.h typedef struct Student { int id; char name[32]; int score; struct Student *next; } Student;typedef的作用是給struct Student起個(gè)別名后面寫函數(shù)簽名時(shí)不用每次帶struct關(guān)鍵字。成員里struct Student *next是指向同一結(jié)構(gòu)體類型的自引用指針這正是鏈表在C語言里能存在的原因。注意這個(gè)指針不能用Student *next直接聲明因?yàn)镾tudent別名在結(jié)構(gòu)體內(nèi)部還沒有定義完雖然某些編譯器能容忍但標(biāo)準(zhǔn)C里不能這么寫。結(jié)構(gòu)體題還會(huì)考察內(nèi)存對(duì)齊name[32]加兩個(gè)int會(huì)讓這個(gè)結(jié)構(gòu)體的大小大于328你可以用sizeof(Student)觀察但不建議在刷題階段用#pragma pack去改對(duì)齊先理解默認(rèn)對(duì)齊規(guī)則更穩(wěn)妥。4.2 鏈表插入、遍歷和釋放先畫出指針指向再寫代碼鏈表題是100例里的分水嶺。我一般建議先畫圖再寫代碼。頭插法建鏈的代碼很好記但面試更常考“反轉(zhuǎn)鏈表”這里給一個(gè)用迭代反轉(zhuǎn)的最小實(shí)現(xiàn)void reverse_list(Student **head) { Student *prev NULL; Student *curr *head; while (curr ! NULL) { Student *next curr-next; curr-next prev; prev curr; curr next; } *head prev; }curr是當(dāng)前節(jié)點(diǎn)next先暫存原來的后繼否則把curr-next指向前驅(qū)時(shí)原始鏈就斷了。最后把頭指針更新為新鏈表的頭。注意參數(shù)為什么是Student **head函數(shù)內(nèi)部要修改調(diào)用方的頭指針單個(gè)*head只能改節(jié)點(diǎn)內(nèi)容改不了head本身。遍歷和釋放鏈表的套路也應(yīng)當(dāng)順手寫上釋放時(shí)先暫存next再free當(dāng)前節(jié)點(diǎn)不然先free再訪問next就是訪存了已釋放內(nèi)存。C語言內(nèi)存管理里這類問題是 valgrind 最容易報(bào)出來的別等面試時(shí)才發(fā)現(xiàn)。4.3 fprintf/fscanf 文件讀寫三條固定步驟少一條都不行結(jié)構(gòu)體和鏈表編完數(shù)據(jù)最終要落盤。文件操作題的標(biāo)準(zhǔn)姿勢是“打開-讀寫-關(guān)閉”每一步都要檢查錯(cuò)誤。void save_students(Student *head) { FILE *fp fopen(students.txt, w); if (fp NULL) { perror(fopen failed); return; } for (Student *p head; p ! NULL; p p-next) { fprintf(fp, %d %s %d\n, p-id, p-name, p-score); } fclose(fp); } void load_students() { FILE *fp fopen(students.txt, r); if (fp NULL) { perror(fopen failed); return; } int id, score; char name[32]; while (fscanf(fp, %d %s %d, id, name, score) 3) { printf(%d %s %d\n, id, name, score); } fclose(fp); }fopen 的w會(huì)截?cái)嘁延形募要求文件必須存在a是追加。fprintf 和 fscanf 格式串要嚴(yán)格對(duì)應(yīng)讀字符串時(shí)%s會(huì)自動(dòng)跳過空白字符但不會(huì)讀取帶空格的字符串這正好適合書本上那種簡單的“一行一條”數(shù)據(jù)格式。判斷讀取成功的條件要寫 3因?yàn)槿绻募崆敖Y(jié)束fscanf 返回匹配成功的項(xiàng)數(shù)會(huì)小于3循環(huán)體就不會(huì)執(zhí)行多余的數(shù)據(jù)。最后fclose不只是釋放資源它還會(huì)把緩沖區(qū)剩余內(nèi)容刷到磁盤漏掉它可能導(dǎo)致文件里缺尾塊。如果你刷到虛擬存儲(chǔ)器管理、流量計(jì)累計(jì)這類偏實(shí)踐的題目文件讀寫往往就扮演數(shù)據(jù)記錄的角色與這里的三步走一致。5. 把100例變成自己的驗(yàn)證集三個(gè)能直接用的小技巧5.1 用 assert 宏給每道題加前置條件刷題時(shí)快速反饋給排序或查找函數(shù)加一行assert(p ! NULL)能快速暴露空指針。比如在鏈表函數(shù)開頭加#include assert.h void reverse_list(Student **head) { assert(head ! NULL); // head 本身不能為空 if (*head NULL) return; // ... }assert在NDEBUG宏定義后會(huì)被整個(gè)取消所以不要把它當(dāng)成最終的錯(cuò)誤處理。刷題階段它能讓錯(cuò)誤盡早暴露尤其是那種“偶爾崩潰偶正?!钡囊爸羔槅栴}。5.2 用 valgrind 驗(yàn)證代碼有沒有內(nèi)存泄漏C語言內(nèi)存管理題里最常見的扣分點(diǎn)是申請(qǐng)了malloc但忘了free。編譯時(shí)加-g然后運(yùn)行g(shù)cc -Wall -stdc11 -g -o demo demo.c valgrind --leak-checkfull --error-exitcode1 ./demo看到 “ERROR SUMMARY: 0 errors” 是基本要求。--error-exitcode1可以讓腳本在檢測到錯(cuò)誤時(shí)返回非零碼以后寫自動(dòng)批改腳本時(shí)直接檢查退出碼就行。這個(gè)命令對(duì)鏈表題尤其重要因?yàn)殒湵淼?free 順序一錯(cuò)valgrind 就會(huì)報(bào) invalid free 或 definitely lost。5.3 把PDF里的題目轉(zhuǎn)成Markdown卡片按知識(shí)塊打標(biāo)簽既然原始資料是PDF常見做法是用pdftotext先提取文本再整理pdftotext -layout C語言經(jīng)典例題100例.pdf examples.txt sed -n 1,80p examples.txt-layout會(huì)盡量保留版面結(jié)構(gòu)后續(xù)再用腳本按“題目編號(hào)”切分轉(zhuǎn)成Markdown卡片時(shí)順手加上#鏈表、#指針標(biāo)簽。PDF里的中文如果亂碼別花時(shí)間調(diào)編碼直接手敲題目信息也很快因?yàn)樽罱K的目的是給每道題建立索引。整理完自己的索引后你會(huì)發(fā)現(xiàn)C語言必背的那些代碼塊都變成了按場景歸類的函數(shù)片段面試前翻一遍自己的驗(yàn)證集比重新啃PDF更有用。本文還有配套的精品資源點(diǎn)擊獲取