# C語言期中考搶救包😭 給對C語言考試還抱有補救心態的人 ## 前言 ### 注意 - 以下程式皆為C的寫法 - 按照順序,底下的概念需要上面的概念 - 電腦看字體大小比較正常 - 純手打無AI,有錯可告訴我 - 還沒寫完的我有空再用 - 有問題當然可以來問我,但我希望來問之前可以先自己查過or問過AI,清楚自己要問什麼 ### 以下為我的廢話可以跳過 印象中上禮拜有人問我資訊課考試可以看什麼 我就只能說基礎語法,我也不知道你們可以看什麼 阿最近要考了感覺聽到蠻多哀號的 突然覺得我可以搞個教學,給還想補救的人 考完APCS,就有時間搞這個了 (結果這東西花我很少時間,兩小時內,主要打字不夠快) 我覺得班上其實蠻多人會程式的 只是感覺沒人再討論就是了 希望想學的是有能被救到啦 ## 變數 變數 就是個可以儲存值,且可以用來運算跟修改的東西 #### 宣告一個變數 用變數前,你需要告訴電腦 要一個 什麼類型 什麼名字 初始值是多少的變數 這個動作叫 宣告 ```c 類型 名字 = 初始值; int a = 0; ``` 類別: 整數 int 小數 float 字元 char 字串 string 布林 bool | 意思 | 類別名 | 說明 | | ---- |:------:| ----------------------------------------------- | | 整數 | int | 就是整數🙂 | | 小數 | float | 就是小數🙂 | | 字元 | char | 單一字或符號,用`' '` ,如 `'a'`、`'/'` | | 字串 | string | 一串字或符號,用`" "`,如 `"Hello world!"` | | 布林 | bool | `true`或`false`,使用需引入標頭 `#include<stdbool.h>`| ## 輸入輸出 C語言的輸入輸出其實不太好搞 使用輸入輸出時需要引入標頭檔`#include <stdio.h>` 關於標頭檔有興趣自行研究 ### 輸入 ```c int a = 0; scacnf(輸入輸出類型,&變數名,可多個變數); scacnf("%d",&a); ``` 類型是字串的格式進去的,所以用" " **重要: & 記得寫,沒寫會出問題且不會報錯** 這邊的類型又跟上面不太一樣的 | 類別名 | 輸入輸出類型 | |:------:| ------------ | | int | %d | | float | %f | | char | %c | | string | %s | 可以發現只有int要特別記一下 這邊要知道一個 %* 代表一個數字,所以可以 ```c int a,b,c; scanf("%d%d%d",&a,&b,&c); // 一行輸入三個 ``` ### 輸出 輸出不用 & ```c printf(類型,變數名) printf("%d",a); printf("\n"); // "\n" 為換行符號 // 字串內為格式,他會把 %* 替換成你給的變數 int a=2,b=1; printf("%d + %d = %d\n",a,b,a+b); // 結果 // 2 + 1 = 3 ``` 類型跟上面一樣 而字串裡面就是輸出格式 特別的是小數輸出,可以對他設置格式 ```c float a = 5.55; printf("%.3f",a); // 輸出至小數點後3位,這個比較好用 //結果: 5.550 printf("%3.1f",a); // 共輸出三位,小數點自身算一位 //結果: 5.6 ``` 有發現一件事嗎,他自動四捨五入了 ~~我高一考試被這搞到~~ 如果不要四捨五入,就不能這樣算 :::spoiler 不要四捨五入參考寫法: ```c float a = 5.55; float b = (int)(a * 10) / 10; // 截斷到小數一位 ``` ::: ## 基本運算 加減乘除 以及 求餘 計算順序是 括號 > 乘除 求餘 > 加減 基本運算的寫法都一樣: ```c int a = 0; int b = 2 a = a + 1; a = a - b; // 運算可以都是變數 a = (a + 1)*b; // 括號優先 a = a / 1; a = a % 7; // 以上都可以簡寫成以下形式 a *= 2; a -= 7; // 當"加減"數字1時,還可以簡寫成 a++; a--; ``` 要注意的是程式的`一個等於` 代表把右邊的值賦予給左邊(稱為賦值) 加減乘用法比較沒什麼問題 特別要注意的是 除 求餘 ### 除 除要特別注意類別問題 如果 整數除整數 是**不會**產生小數的(無條件捨去) 如 `5/2 = 2` 整數除小數 或 小數除小數 就沒問題 如何讓整數除整數,需要保留小數? 把整數轉成小數 ```c int a = 5; float b = 5 / 2.0; // 除2改成除2.0 int c = 2; // 除數是變數 float t = c; // 轉小數 float d = a / t; // 也可以用強制轉換: (轉換類型)變數 // 課上可能沒教? 我不知道 float d = a / (float)c; ``` ### 求餘 求餘比較特別,取左邊÷右邊的餘數 舉例 7/2 = 3...1 所以 7%2 = 1 寫法跟上面運算都一樣 ```c int a = 5; a %= 2; // a=1 ``` 求餘x可以判斷是否整除x 底下章節再詳細講 ## 條件運算、邏輯運算 結果會是布林值 ### 條件(比較): ```c bool result = 1>=2; //false //也可以是變數 int a=10,b=6; result = a > b; //true ``` 比較運算子: ```c > 大於 >= 大於等於 < <= == <- 要注意兩個等於才是比較 ``` 一個等於是賦值 絕對不能寫錯(不小心寫錯很難發現) ### 邏輯 ```c bool a = true; bool b = false; bool c = a || b; // c = true c = a && b; // c = false c = ( a || b ) && a; //括號優先 c = true ``` | 邏輯運算子 | 意思 | | ---------- | ------- | | && | 與(and) | | \|\| | 或(or) | ## 條件判斷 讓程式依照不同情況運行不同內容 將會用到上面的條件運算、邏輯運算 結構: ```c if(布林值){ }else if(布林值){ }else{ } int a = 75; if(a >= 90){ printf("A"); }else if(a>=80){ printf("B"); }else{ printf("C"); } // 結果: // C ``` if系列有三個 `if` `else if` `else` 並沒有一定要都出現,也沒有限制要幾個 視情況自己判斷,但要寫`if`就是會出現`if` 不會出現沒有`if`的 `else if` 跟 `else` **很重要:要記得寫小掛號跟大掛號** 條件判斷很常跟求餘一起用 ```c int a = 10; if (a%5 == 0){ //判斷整除 } int b = 3; if (b%2 == 1){ // 是奇數 }else{ // 是偶數 } ``` ### 巢狀 if系列內可以再套if系列 但要注意else是給誰 這時候`縮排`就很重要了 ```c if(){ if(){ }else{ } }else{ } ``` ## 迴圈 重複做很多次同樣內容 **很重要:要記得寫小掛號跟大掛號** ### for 結構: ```c for(變數宣告 ; 持續條件 ; 每次循環改變變數){ } for(int i=0 ; i<10 ; i++){ printf("%d ",i); //注意:這個輸出格式有%d旁有空格 } // 輸出結果: // 0 1 2 3 4 5 6 7 8 9 for(int i=10 ; i>=0 ; i--){ printf("%d ",i); } // 輸出結果: // 10 9 8 7 6 5 4 3 2 1 0 int n; scanf("%d",&n); for(int i=1;i<=n;i++){ printf("%d ",i); } // 結果: // 輸出 1~n ``` 當 `持續條件` 為false時跳出迴圈 同樣的for一樣可以巢狀 但注意兩層變數名要不同 ```c for(int i=0 ; i<4; i++){,, // 這裡是 i for(int j=0 ; j<4 ; j++){ // 這裡是 j printf("%d ",i*j); } printf("\n"); } // 結果: // 0 0 0 0 // 0 1 2 3 // 0 2 4 6 // 0 3 6 9 ``` ### while while只靠一個布林值來判斷是否執行 透過內部邏輯來改變判斷條件 如果內部沒有改變會導致無限迴圈 結構: ```c while(布林值){ //記得要能改變上面的條件 } int a = 99; while(a>0){ printf("%d ",a); a /= 2; } // 輸出: // 99 49 24 12 6 3 1 ``` 同樣可以巢狀 ### continue break continue 和 break 可以在迴圈中跳出 continue是跳過一次當前迴圈,整個迴圈依然繼續 break會直接離開一個迴圈 要注意break只會跳離一個 如果是巢狀,並不會跳出所有 兩個都只能在迴圈中使用 ```c for(int i=0;i<10;i++){ if(i%2==0){ continue; //跳過一次 }else if(i>=4){ break; //離開迴圈 } printf("%d",i); } // 結果: // 1 3 ``` ### 該用for還是while? for適合確定迴圈次數的情況 while適合不知道要迴圈幾次 ## 其他 ### return return將會退出函式 函式有興趣自行研究 現在會用到的是 `int main(){}` 這是一個函式 所以在裡面用 `return 0;` 會退出整個程式 ### 常用函式 #### 根號 需引入`#include <math.h>` `sqrt();` 小括號內放數字,輸出根號值 #### 絕對值 需引入`#include <stdlib.h>` `abs();` 小括號內放數字,輸出絕對值 # 上次的題目+題解 ## 韓信點兵 韓信點兵後,發現兵的數量呈現以下規則 5人一組剩3人 7人一組剩2人 11人一組剩5人 問1~10000那些兵數滿足條件 :::spoiler 題解 5人一組3人就是 人數 / 5 的餘數 -> n%5 == 3 是否為true 所以用if判斷是否三個條件是否同時成立就好 人數可以可能是1\~10000,所以for迴圈1~10000 ::: :::spoiler 完整程式 ```c= #include <stdio.h> int main() { for(int i=1;i<=10000;i++){ if(i%5==3 && i%7==5 && i%11==5){ printf("%d\n",i); } } return 0; } ``` ::: ## 電影院座位 Amy去看電影,影廳的座位是8個一排,共12排,96個位置,所有座位按照 順序編號。請輸入Amy的座位編號(1-96),印出她的座位在第幾排第幾個位置。(作業題) :::spoiler 題解 觀察一下可以發現編號是 1~8 -> 第一排第1~8 9~16 -> 第二排第1~8 ...以此類推 可以發現排數是 (n/8)+1 但當n=8時上面的式子會不對 所以要特別處理當n整除8的情況 ->排數為 (n/8) 第幾個則是 n%8 同樣特別處理n整除8的情況 -> 個數為8 ::: :::spoiler 完整程式 ```c= #include <stdio.h> int main() { int n,r,c; scanf("%d",&n); if (n%8 != 0){ r = n/8+1; c = n%8; } else { // 整除情況 r = n/8; c = 8; } printf("%d %d\n",r,c); return 0; } ``` ::: ## 質數 輸入一整數,輸出是否為質數 是的話輸出"yes"否則"no" :::spoiler 題解 質數定義為除了1跟自己(n),還能整除其他數 所以只要迴圈1~n,每個數字都判斷一下能不能整除 能整除的話代表不是質數跳出迴圈,能的話繼續 要注意不能在迴圈中輸出,除非你確定輸出之後不會在繼續迴圈 否則會重複輸出 ::: :::spoiler 完整程式 法一: 使用bool變數 ```c= #include <stdio.h> #include <stdbool.h> //bool int main() { int n; scanf("%d",&n); bool is_prime = true; for(int i=2;i<n;i++){ if(n%i == 0){ is_prime = false; break; } } if(is_prime){ printf("yes"); }else{ printf("no"); } return 0; } ``` 法二:使用return來確保不會重複輸出 ```c= #include <stdio.h> int main() { int n; scanf("%d",&n); for(int i=2;i<n;i++){ if(n%i == 0){ printf("no"); return 0; } } printf("yes"); return 0; } ``` ::: ## 分薯條 子路有23根薯條,想分給給他4個朋友 社長最多拿8 冠頡最多拿7 家瑋最多拿6 章宇最多拿5 問有幾種方法可以分 :::spoiler 題解 ~~子路我要吃薯條~~ 如果排列組合厲害的話可以用數學算出答案 程式最簡單的解法是四層for迴圈,代表四個人拿到的薯條 循環出所有滿足條件的答案 怎麼知道是否滿足條件? 四個for迴圈的變數相加等於23就是滿足條件 另外我記得有人會用+-來跑這個 當然可以,那是更好的做法,只是實作比完全暴力解還難 這題很神奇的是,有沒有限制能不能不拿薯條,出現的答案是一樣的 我是懶得想為什麼www ::: :::spoiler 完整程式 ```c= #include <stdio.h> int main() { int count = 0; for(int i=0;i<=8;i++){ for(int j=0;j<=7;j++){ for(int k=0;k<=6;k++){ for(int l=0;l<=5;l++){ if(i+j+k+l == 23){ count++; } } } } } printf("%d",count); return 0; } ``` ::: ## 會長高的怪植物 班上在種綠豆,有一盆綠豆變異成怪植物了 怪植物 第一天會長高 原高度的一半 第二天會長高 原高度的一半又一半 每天長高的長度 會是 上一天的一半 當長高長度 $<0.5$ 時,植物就沒救了,停止生長 輸入植物的長度,輸出最後植物的長度 :::spoiler 題解 先計算出一天會長多少,一天一天的加到原長度上就是答案了 由於原長度不限,所以也不能確定要幾天才會<0.5,所以用while 每天生長長度: $l = l/2$ (初始=原長度) 每天長度: $原長度$ += $l$; ::: :::spoiler 完整程式 ```c= #include <stdio.h> int main() { float n; //長度 scanf("%f",&n); float l = n; //每天生長長度 初始 = 原長度 while(l>=0.5){ l /= 2; n += l; } printf("%f",n); } ``` ::: ## 密碼差 輸入一整數,輸出這串數字中 $|奇數位總和 - 偶數位總和$| :::spoiler 題解 這題難點在於分出奇數位和偶數位 方法很多,這邊提供我自己的做法 個位數的取法是 n%10 ,這樣能取到個位數 取完之後 n/10,捨棄掉個位數 再次取個位數,這時會取到原先的十位數 重複直到 n==0 結束 可以發現每重複一次,會取到 奇數位 -> 偶數位 -> 奇數位 -> ... 只要這次是奇,下次一定是偶 ...以此類推 所以只要建立兩個總和變數,判斷當前奇偶,加上對應的變數 最後再取差就好 ::: :::spoiler 完整程式 ```c= #include <stdio.h> #include <stdbool.h> #include <stdlib.h> int main() { int n; scanf("%d",&n); int odd_sum = 0; int even_sum = 0; bool is_odd = true; while(n!=0){ //不確定跑幾次用while if(is_odd){ odd_sum += n%10; is_odd = false; }else{ even_sum += n%10; is_odd = true; } n /= 10; } // 絕對值: abs() printf("%d", abs(odd_sum - even_sum) ); } ``` :::