# 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) );
}
```
:::