Try   HackMD
tags: 創造力資優課程
tags: computer

C 語言課程

龍興國中 創造力資優班 C 語言課程

線上程式語言編譯平台: onlinegdb

學習 C 語言的好處簡報

1. C語言初體驗 :

  • 您可以編輯程式碼並在onlinegdb中執行並查看結果:
#include <stdio.h>

int main() {
  printf("Hello World!");
  return 0;
}

2. 什麼是C語言?

  • C 是丹尼斯·里奇於 1972 年在貝爾實驗室創建的通用程式

  • 儘管它很古老,這是一種非常流行的程式語言。

  • C 與 UNIX 密切相關,因為它是為編寫 UNIX 作業系統而開發的。

3. 為什麼要學習 C?

  • 它是世界上最流行的程式語言之一2023程式語言排名
  • 如果你懂 C,那麼學習其他流行的程式如 Java、Python、C++、C# 等也沒有問題,因為語法相似
  • 與其他程式Java和Python)相比,C 語言執行速度非常快
  • C 語言是通用的;它可以在不同硬體上的應用程式運作
  • 學習 C 語言對於 APCS「大學程式設計先修檢測」 觀念題概念比較清楚

4.C 和 C++ 之間的區別

  • C++是 C 的擴展開發的版本,兩種語言的語法幾乎相同
  • C 和 C++ 的主要區別在於 C++ 支持class和object,而 C 不支持

5.程式範例說明

1.#include <stdio.h>
2.
3.int main() {
4.  printf("Hello World!");
5.  return 0;
6.}
  • 第 1 行: #include <stdio.h> 是一個函數庫,可讓我們呼叫輸入和輸出的各種函數。
    • 例如 printf()(在第 4 行)。在 C 語言 函數庫中呼叫 printf() 函數功能。

如果您不了解其 #include <stdio.h>工作原理,請不要擔心。只要把它想像成(幾乎)總是出現在你的 c 程式中的東西。

  • 第 2 行: 空行。C語言會忽略空行。但是我們使用空行會更容易讀懂程式內容。
  • 第 3 行:另一個總是出現在 C 語言程式中的東西是 int main(){ } 這稱為 函數。大括號 { } 內的任何程式都會被執行
  • 第 4 行: printf() 是將資料輸出到螢幕的函數。在我們的例子中,它將輸出Hello World

請注意:每句 C 語言程式碼都是以分號 ; 結尾

  • 第 5 行: return 0結束 main( ) 函數。

  • 第 6 行:不要忘記添加右大括號 } 以實際結束 main 函數。

6.輸出

  • printf()函數用於輸出數值文字
#include <stdio.h>

int main() {
  printf("Hello World!");
  printf("I am learning C.");
  return 0;
}

結果: Hello World!I am learning C.

  • 換行符號 \n ,它強制游標將其位置更改到螢幕中下一行的開頭。產生一個新的一行。
#include <stdio.h>

int main() {
  printf("Hello World!\n");
  printf("I am learning C.");
  return 0;
}

結果:

Hello World!
I am learning C.

7.註解

  • 註解是用來解釋程式碼,並使其易於了解。它還可用於在測試替代程式碼時暫時停止執行。
    • 註釋可以是單行或多行的
  • 單行註解:以兩個 // 開頭,編譯器不會執行這一行程式碼
// 向世界問好
printf("Hello World!");


printf("Hello World!"); // 向世界問好
  • 多行註釋以開頭 /* 和結尾 */

    • /* 和之間的任何程式碼或文字 */ 都將被編譯器忽略而不執行:
/* 下面的程式碼將會輸「Hello World!」字樣
到螢幕,真是太神奇了 */
printf("Hello World!");

8.變數

  • 變數可儲存被指定的數據值

在 C語言中,不同類型的變數,有規定的宣告方式 例如: int 儲存整數,例如 123 或 -123 ; float 儲存浮點數,帶小數點的數字,例如 19.99 或 -19.99 ; char 儲存單個字元,例如'a' or 'B'。字元值用單引號括起來

  • 建立變數 a 以int方式並指定值為30
int a = 30;
  • 也可建立一個變數而不直接指定值,之後再指定數值:
int a;
a = 30;
  • 輸出 int 變數的值,必須在函數內部使用格式說明符號 %d%i 用雙引號括起來printf():
int a = 30;
printf("%d", a); 
  • 輸出 char 變數的值,必須在函數內部使用格式說明符號 %c 用雙引號括起來printf():

  • 輸出 float 變數的值,必須在函數內部使用格式說明符號 %f 用雙引號括起來printf():

// 建立變數
int myNum = 5;             // 整數
float myFloatNum = 5.99;   // 浮點數
char myLetter = 'D';       // 字元

// 輸出變數
printf("%d\n", myNum);
printf("%f\n", myFloatNum);
printf("%c\n", myLetter);
  • 加總變數:要將兩個變數加總,可以使用+ 運算符號
int x = 5;
int y = 6;
int sum = x + y;
printf("%d", sum);
  • 宣告多個變數:建立多個變數時,請使用逗號分隔
int x = 5, y = 6, z = 50;
printf("%d", x + y + z);//112.11.10-903

9.變數的命名規則

  • 所有變數都必須指定唯一的識別碼。
  • 識別碼可以是短代碼(如 x 和 y)或具描述性的代碼(age、sum、totalVolume)。 建議使用描述性代碼讓程式碼易於理解和容易維護 例如:

int minutesPerHour = 60; // 很棒


int m = 60;  // OK, 但不是很容易理解變數的內容
  • 命名變數的一般規則是:

名稱可以包含字母、數字和下劃線_ 名稱必須以字母或下劃線 (_) 開頭 名稱區分大小寫(myVar 和 myvar 是不同的變數, 名稱不能包含空格特殊字符,如 !、#、% 等。 保留字(如int)不能用作名稱//111.9.28

10.資料型態

  • 宣告變數時要指定資料型態,並且在printf()中使用相關格式說明符號顯示:
// 宣告變數
int myNum = 5;             // 整數
float myFloatNum = 5.99;   // 浮點數
char myLetter = 'D';       // 字元

// 顯示列印變數
printf("%d\n", myNum);
printf("%f\n", myFloatNum);
printf("%c\n", myLetter);

11.常數

  • 將變數宣告為常數,意味只能讀取不可更改
const int myNum = 15;  // myNum 永遠是15
myNum = 10;            // 錯誤: myNum 不可以再指定其他數值
  • 當變數的值永遠固定不能變更時,即可將該變數宣告為常數
const int minutesPerHour = 60;//一小時為60分鐘
const float PI = 3.14; //圓周率=3.14
  • 當宣告一個常數時,必須指定數值:
const int minutesPerHour = 60; //正確
const int minutesPerHour;
minutesPerHour = 60; // 錯誤

12.運算子

  • 例子:我們使用 + 運算子將兩個數值相加:
int myNum = 100 + 50;
  • 就像下面的例子一樣,一個變數可以和一個數值相加,或者一個變數相加另一個變數
int sum1 = 100 + 50;        // 答案: 150 (100 + 50)
int sum2 = sum1 + 250;      // 答案: 400 (150 + 250)
int sum3 = sum2 + sum2;     // 答案: 800 (400 + 400)

算數運算子

運算子 運 算 Example
+ 加法 x + y
- 減法 x - y
* 乘法 x * y
/ 除法 x / y
% 餘數 x % y
++ 增加1 ++x
- - 減1 - -x

賦值運算子

  • 使用賦值運算子 = 分配 10 給名為x的變數:
int x = 10;
  • 加法賦值運算子 += 將一個數值添加到變數中:
int x = 10;
x += 5;  //答案是 15
運算子 例 子 等同
= x =5 x =5
+= x +=3 x=x+3
-= x -=3 x=x-3
*= x *=3 x=x*3
/= x /=3 x=x/3
%= x %=3 x=x%3

比較運算子

比較運算子用於比較兩個數值。

注意:比較的結果可能回傳值為 true (1) 或 false ( 0)。

在以下示例中,我們使用大於運算子 > 來確定 5 是否大於 3:

int x = 5;
int y = 3;
printf("%d", x > y); // 回傳值為 1 (true) 因為 5 確實會大於 3
運算子 意義 例子
== 等於 x == y
!= 不等於 x != y
> 大於 x > y
< 小於 x < y
>= 大於或等於 x >= y
<= 小於或等於 x <= y

邏輯運算子

邏輯運算子用於確定變量數值之間的邏輯:

運算子 名稱 意義 舉例      |
&& 兩者都成立 x < 5 && x < 10
|| 兩者其中之一成立 x < 5 || x < 4
! 不是 回傳相反值 !(x < 5 && x < 10)

Sizeof 運算符

  • sizeof運算子找到數據類型或變數的暫存大小
int myInt;
float myFloat;
double myDouble;
char myChar;

printf("%lu\n", sizeof(myInt));
printf("%lu\n", sizeof(myFloat));
printf("%lu\n", sizeof(myDouble));
printf("%lu\n", sizeof(myChar));

13.if else

比較運算子章節中可以知道 C 語言中常用的邏輯條件:

  • 小於: a < b
  • 小於或等於: a <= b
  • 大於: a > b
  • 大於或等於: a >= b
  • 等於 a == b
  • 不等於: a != b

C語言有以下條件語句:

  • 使用 if 如果指定條件為,執行指定的區塊程式
  • 使用 else 如果指定條件為,執行指定的區塊程式
  • 使用 else if 如果第一個條件為假,測試新的條件
  • 使用 switch 指定執行多個不同的區塊程式

if 的語法

if (指定條件) {
  // 如果指定條件為真,執行指定此區塊程式。
}

if 的例子

  • 在下面的例子中,我們測試 20 是否大於 18。如果條件為true,則印出"20 is greater than 18"
  • 注意 if 是小寫字母。大寫字母(If 或 IF)會產生錯誤。
if (20 > 18) {
  printf("20 is greater than 18");
}

  • 使用變數測試,結果成功
int x = 20;
int y = 18;
if (x > y) {
  printf("x is greater than y");
}

練習題:

1.如何判斷一個給定的數字是偶數還是奇數?

  • 這是一個經典的C語言程式。 我們將在C語言中學習使用條件語句 if ,將一個整數除以2,如果整除(餘數為零),則該數是偶數,如果不能整除則該數是奇數。

2.輸入一個整數後即輸出該數的絶對值

  • 不論該整數是正數或負數圴輸出正數

3.判斷三條線段是否可圍成一個三角形

  • 3,4,5 可
  • 6,6,12 不可
  • 3,4,8 不可

if else 語法

if (指定條件) {
  // 如果指定條件為真,執行指定此區塊程式。
} else {
  // 如果指定條件為假,執行指定此區塊程式。
}

例子

int time = 13;
if (time < 12) {
  printf("早安.");
} else {
  printf("午安.");
}
// 輸出 "午安."
  • 在上面的例子中,時間 13 大於 12,所以條件是false。因此,我們執行else區塊程式顯示出“午安”。如果時間小於 12,程式將顯示“早安”。

練習題:

如何判斷一個給定的數字是偶數還是奇數?

  • 這是一個經典的C語言程式。 我們將在C語言中學習使用條件語句if-else。將一個整數除以2,如果整除則是偶數,否則是奇數。

在三個數字中,找出最大的一個數字

  • i : 3
  • J : 4
  • K : 5

ifelse ifelse 的語法

if (指定條件1) {
  // 如果指定條件1為真,執行指定此區塊程式。
} else if (指定條件2) {
  // 如果指定條件1為假,指定條件2為真,執行指定此區塊程式。
} else {
  //如果指定條件1為假,指定條件2為假,執行指定此區塊程式。 
}
  • 例子
#include <stdio.h>

int main()
{
    int time = 22;
    if (time < 12) {
        printf("Good morning.");
    } 
    else if (time < 20) {
        printf("Good day.");
    }
    else {
        printf("Good evening.");
    }
        // 輸出 "Good evening."
}
  • 在上面的例子中,時間 (22) 大於 12,所以第一個條件是false。語句中的下一個條件 else if也是false,因此我們繼續處理else 條件,因為條件1和條件2 都是false,所以螢幕顯示“Good evening.”。但是,如果時間是 14,程式執行結果會是螢幕顯示“Good day”。

練習題

1.判斷 3 條線段是否可圍成三角形:

  • 若可以圍成三角形則為下列何者1.正三角形,2.等腰三角形,3、直角三角形,4、普通三角形

ifelse 簡寫法與三元運算子

  • 語法
變數 = (條件) ? True的結果 : False的結果;
  • 例子
int time = 20;
if (time < 18) {
  printf("Good day.");
} else {
  printf("Good evening.");
}
  • 本例的簡略的表達方式
int time = 20;
(time < 18) ? printf("Good day.") : printf("Good evening.");

14.switch

下午1.49 2024-11-20的影像

  • 語法
switch(變數名稱) {
    case 符合數字:
        陳述句一;
        break;
    case 符合數字:
        陳述句二;
        break;
    default:
        陳述三;
        break;
}
  • 首先看看 switch 的括號,當中置放要取出數值的變數或運算式,取得數值之後,會與 case 設定的數字或字元比對,如果符合就執行以下的陳述句,直到遇到 break 後離開 switch 區塊,若沒有符合的數值或字元,會執行 default 後的陳述句,default 不需要時可以省略。
  • 例子
int day = 4;

switch (day) {
  case 1:
    printf("Monday");
    break;
  case 2:
    printf("Tuesday");
    break;
  case 3:
    printf("Wednesday");
    break;
  case 4:
    printf("Thursday");
    break;
  case 5:
    printf("Friday");
    break;
  case 6:
    printf("Saturday");
    break;
  case 7:
    printf("Sunday");
    break;
}

// Outputs "Thursday" (day 4)
  • 例子2
int rank = 4;

switch (rank) {
  case 1: case 2: case 3:
    printf("電腦作業得95分");
    break;
  case 4:  case 5:
    printf("電腦作業得90分");
    break;
  case 6:
    printf("電腦作業得80分");
    break;
  }

// Output "電腦作業得90分" (rank 4)
  • 關鍵字 break ,當程式執行到達關鍵字 break 時,它會跳出 switch 區塊。停止在switch區塊內執行更多程式並忽略其他 case 的測試。

練習題

1.兩光法師占卜術

  • 兩光法師時常替人占卜,由於他算得又快有便宜,因此生意源源不絕,時常大排長龍,他想算 得更快一點,因此找了你這位電腦高手幫他用電腦來加快算命的速度。

他的占卜規則很簡單,規則是這樣的,輸入一個日期,然後依照下面的公式:

M=月 D=日 S=(M*2+D)%3

得到 S 的值,再依照 S 的值從 0 到 2 分別給與 普通、吉、大吉 等三種不同的運勢

break 可以節省大量執行時間,因為它“忽略”了 switch 區塊中其餘程式的執行。

  • 關鍵字 default,如果没有任何 case 相符時,則執行一些預設程式。
  • 例子
int day = 4;

switch (day) {
  case 6:
    printf("Today is Saturday");
    break;
  case 7:
    printf("Today is Sunday");
    break;
  default:
    printf("Looking forward to the Weekend");
}

// Outputs "Looking forward to the Weekend"

default 關鍵字必須是 switch 區塊中的最後一段陳述,並且不一定需要 break。

15.While 迴圈

  • 語法
while (條件) {
  // 符合條件下會執行區塊內程式
}
  • 例子
int i = 0;

while (i < 5) {
  printf("%d\n", i);
  i++;
}
  • 關於C語言中的while迴圈while1是什麼意思:
  • while(1)代表了迴圈永遠執行下去.除非遇到break;才跳出迴圈.原因是while的迴圈裡面是一個布林值,而1代表了true,所以是一個無限迴圈.

dowhile 迴圈

  • 語法
do {
  // 先執行區塊內程式一次,若條件符合則持續執行下去
}
while (條件);
  • 例子
int i = 0;

do {
  printf("%d\n", i);
  i++;
}
while (i < 5);

16. for 迴圈

  • 當明確知道重複執行一段程式的次數時,則使用 for 迴圈而不是 while 迴圈:
  • 語法
for (陳述句1 ; 陳述句2 ; 陳述句 3) {
  // 執行區塊內的程式
}

例子 1

int i;

for (i = 0; i < 5; i++) {
  printf("%d\n", i);
}

例子 1 解說:

  • 陳述句 1 : 在迴圈開始之前建立一個變數,名為 i ,起始值為 0 ( int i = 0)。
  • 陳述句 2 : 定義了迴圈運行的條件(i 必須小於 5)。如果條件為,迴圈將重複執行,如果條件為假,迴圈即結束。
  • 陳述句 3 : 每次執行迴圈一次,i 的值都會增加 1 ( i++ )。

例子 2 :本例僅會列印 0 到 10 之間的偶數

for (i = 0; i <= 10; i = i + 2) {
  printf("%d\n", i);
}

練習題

1.判斷一個整數,是質數還是合數,並找出所有的因數

  • 質數只有1和該數本身共 2 個因數。

2.計算 1 加到 10 的總和

  • 限用 for 迴圈完成。

3. 身份證字號真偽判斷

我國的身分證字號有底下這樣的規則,因此對於任意輸入的身分證字號可以有一些基本的判斷原則,請您來判斷一個身分證字號是否是正常的號碼(不代表確有此號、此人)。

(1) 英文代號以下表轉換成數字

​​​​  A=10 台北市     J=18 新竹縣     S=26 高雄縣
​​​​  B=11 台中市     K=19 苗栗縣     T=27 屏東縣
​​​​  C=12 基隆市     L=20 台中縣     U=28 花蓮縣
​​​​  D=13 台南市     M=21 南投縣     V=29 台東縣
​​​​  E=14 高雄市     N=22 彰化縣     W=32 金門縣
​​​​  F=15 台北縣     O=35 新竹市     X=30 澎湖縣
​​​​  G=16 宜蘭縣     P=23 雲林縣     Y=31 陽明山
​​​​  H=17 桃園縣     Q=24 嘉義縣     Z=33 連江縣
​​​​  I=34 嘉義市     R=25 台南縣

(2) 英文轉成的數字, 個位數乘9再加上十位數的數字

(3) 各數字從右到左依次乘1、2、3、4....8

(4) 求出(2),(3) 及最後一碼的和

(5) (4)除10 若整除,則為 real,否則為 fake

例: T112663836

2 + 7*9 + 1*8 + 1*7 + 2*6 + 6*5 + 6*4 + 3*3 + 8*2 + 3*1 + 6) = 180

除以 10 整除,因此為 real

4.找零機器

請設計一個找零機器的判斷程式,判斷使用者輸入的金額( money)當中,以最少的銅板數來找零錢。該機器所提供的銅板有 50 元、10 元、5 元及 1 元。當輸入的金額數介於 1 到 1000 之間,則依序顯示該找的各個銅板數(50 元、10 元、5 元及 1 元);當金額為 0 時,則結束判斷。

17.Break 和 Continue

break : 前面章節中 break 可“跳出” switch 區塊程式。

  • break 還可用於跳出 while 、for 迴圈

  • 下面例子在 i 等於 4 時跳出迴圈

int i;

for (i = 0; i < 10; i++) {
  if (i == 4) {
    break;
  }
  printf("%d\n", i);
}

練習題

1.找出兩整數的最大公因數

  • 變數設為num1,num2,餘數的變數設為remainder_

2.找出兩整數的最小公倍數

continue: 如果出現特定的條件,continue 會中斷一次迴圈結果,並繼續之後的迴圈。

  • 下面例子在 i 等於 4 時,跳過 4 的值:
int i;

for (i = 0; i < 10; i++) {
  if (i == 4) {
    continue;
  }
  printf("%d\n", i);
}

18.陣列

  • 陣列 : 可以在一個變數中儲存多個數值,而不用為每個數值宣告獨立的變數。
  • 要建立陣列,要先定義數據類型(如int)並指定陣列名稱,後方加 []。
  • 要在[]中加入數值,需在()內使用逗號分隔:
  • 例子:建立四個整數的陣列,陣列名稱為myNumbers。
int myNumbers[] = {25, 50, 75, 100};

依據陣列索引號,存取陣列元素。

  • 陣列索引號從 0 開始:[0] 是第一個元素。[1] 是第二個元素,依此類推。
  • 下面例子是存取myNumbers陣列中的第一個元素 [0]的值:
int myNumbers[] = {25, 50, 75, 100};
printf("%d", myNumbers[0]);

// 輸出答案為 25

更改陣列的元素

  • 要依據索引號更改特定元素的值。
int myNumbers[] = {25, 50, 75, 100};
myNumbers[0] = 33;

printf("%d", myNumbers[0]);

// 輸出結果為 33 取代原本的 25

搜尋陣列全部的元素

  • 可以使用 for 迴圈搜尋陣列全部元素 。

以下例子輸出myNumbers 陣列中的所有元素:

int myNumbers[] = {25, 50, 75, 100};
int i;

for (i = 0; i < 4; i++) {
  printf("%d\n", myNumbers[i]);
}

先設定陣列規模大小的陣列建立方法

  • 另一種常用建立陣列的方法,先指定陣列的大小,然後再增添元素:
  • 例子:
/ 宣告 4 個整數的陣列:
int myNumbers[4];

// 增添元素在陣列中
myNumbers[0] = 25;
myNumbers[1] = 50;
myNumbers[2] = 75;
myNumbers[3] = 100;

陣列的練習

#include <stdio.h>

int main()
{
    int myNumbers[3] ;
    for (int j = 0; j < 4; j++){
    scanf("%d",&myNumbers[j]);
    }
     myNumbers[1] = 33;
     for (int i = 0; i < 4 ; i++){
    printf("%d\n" , myNumbers[i]);
         
     }
}
  • 使用這種建立陣列的方法,必須先確定陣列的大小,才有足夠的記憶體空間讓程式去儲存。
  • 建立陣列後,不能再更改它的規模大小。

練習題

1.費波那契數列

19.字串

20. scanf() 使用者輸入

  • 你已經學過,用 printf() 輸出數值的方法。

  • 要用戶輸入數值,可以使用 scanf()

    晚上9.50 2025-4-2的影像

  • 例子

// 建立一個資料類型為整數的變數,以便儲存使用者所輸入的數值
int myNum;

// 詢問使用者要輸入一個數字
printf("Type a number: \n");

// 獲取且儲存使用者輸入的數字
scanf("%d", &myNum);

// 輸出使用者輸入的數字
printf("Your number is: %d", myNum);
scanf()函數有兩個參數:變數的格式說明符 %d 和引用運算符  &myNum,儲存變數的記憶體位址。

21.字元陣列

22.有限狀態機

23.函式

1. 函式介紹

  • 在C語言中函式相當重要,可以減少許多繁雜的步驟,將程式變得容易閱讀理解。
  • printf(),本質上也是一個函式。(利用 include <stdio.h> 呼叫函式庫來使用)

2.函式的特色:

  • 指定一段程式碼,執行特定的工作。

  • 可重複呼叫使用

  • 讓程式變得容易閱讀理解。

  • 在呼叫函式條件不改變的狀態下,可以直接修改函式的程式。

  • 函式分為系統定義函式(如printf)和使用者定義函式兩種。

  • 系統定義函式(標準函式庫): 需呼叫標頭檔例如:

    • #include <stdio.h>
    • #include <stdlib.h>
    • #include <math.h>
  • 標準函式庫是C語言標準提供的函式庫,包含了大量的函式,可以幫助開發者簡化程式設計,加速開發速度。標準函式庫通常可以被編譯器直接調用,而不需要開發者自己編寫相關的代碼。

  • C語言的標準函式庫可以分為幾個部分,每個部分包含了不同類型的函式:

  • 標準輸出/輸入函式庫:包括了與螢幕和鍵盤輸入輸出有關的函式,例如printf、scanf等。

  • 字符串函式庫:包括了與字串處理有關的函式,例如strlen、strcpy、strcat等。

  • 數學函式庫:包括了與數學運算有關的函式,例如sqrt、sin、cos等。

  • 時間函式庫:包括了與時間有關的函式,例如time、ctime、gmtime等。

  • 檔案操作函式庫:包括了與檔案操作有關的函式,例如fopen、fclose、fread等。

  • 其他函式庫:還包括了與動態記憶體分配、環境變數、數據類型轉換等相關的函式。

  • 使用者定義函式:

  • 回傳值:回傳一個int的型態回去主程式

在下方的程式中,當我們程式跑到 f(4)中,代表他呼叫了上方 int f(int x) 函式,而因為這個函式有回傳值,所以在進行計算過後,我們有一個return x+3,把計算過後的答案傳回到主程式中 f(4) 就會等於回傳值 7,印出答案後程式結束執行。

  • 函式定義: 使用者定義函式

int f(int x){ return x +3; }

int main() {
    printf("%d",f(4));
    return 0;
}
  • 函式宣告:
  • 主程式上方,宣告函式原型(程式是由上而下讀取,告訴主程式說我設的函式的大概內容),參數可省略,主程式下方才放置使用者定義的函式
#include <stdio.h>

int f(int ); //有回傳值的函式宣告

int main() {
    printf("%d",f(4));
    return 0;
}
//以下是函式定義
int f(int x){
    return x +3;
}

3.回傳值型態

沒有回傳值void square(int len)

  • 在下方的程式中,當我們程式跑到square(length)中,代表他呼叫了下方void square函式,而因為這個函式沒有回傳值,所以在印出答案中後,就直接結束程式的執行了(可以想像有一個return 0 函式下方)。
    上午9.50 2024-3-6的影像

有回傳值int square(int length)

  • 回傳一個 int 的型態給 主程式
    上午9.52 2024-3-6的影像

4. 函式宣告

  • :寫一個計算正方形的程式,並且使用函式呈現。
  • 在主程式上方,宣告函式(跟主程式說我建立的函式樣式如何)
  • 再從主程式下方定義函式
 回傳值型態 函式名稱 (參數) 

上午10.19 2024-3-6的影像

練習題:利用函式寫一個判斷閏年的程式

  • 輸入: 2011
  • 輸出: 0
#include <stdio.h>//閏年的規則,每四年閏一次,但是每一百年不閏,遇到四百年還是要閏

int main()
{
    //int year = 112;
    int year;
    printf("請輸入任一年的數字");
    scanf("%d",&year);
    if ( year % 400 == 0){//除以400的餘數為0,則為閏年
        printf ("閏年");
    }
    else if (year % 100 == 0){//再除以100的餘數為0,則為平年
        printf ("平年");
    }
    else if (year % 4 == 0){//再除以4的餘數為0,則為閏年
       printf("閏年"); 
    }
    else {
        printf("平年");//餘為平年
    }
}

練習寫一個計算面積的公式1,公式2

  • 公式: square = len *len
  • 無回傳值的函式

在函式原型宣告時,第一個宣告的元素就是函式的傳回值的資料型別,任何的基本資料型別(如int, char, float, double等)或自訂的資料型別都可以使用,但是如果函式不傳回任何值,則我們要把資料型別宣告為void,意思是不傳回值。

#include <stdio.h>

void f(int ); //没有回傳值的函式宣告

int main() {
    f(4);
    return 0;
}
//以下是函式定義
void f(int x){
    printf("%d",x+3);
    return ;
}

練習寫一個無回傳值的計算面積公式

  • 公式: square = len *len
  • 兩個參數的函式
int add(int x, int y) {   // 函式定義為 add,参數列表為 x 和 y
    int result = x + y;   // 將 x 和 y 相加,得到結果
    return result;        // 將結果回傳給函式呼叫者
}
int main(){
    int sum = add(3, 5);   // 呼叫函數 add,並將結果指定給變數 sum 


}

函式影片1

函式解說1

函式影片2

從費氏數列認識何謂遞迴-簡報

  • 根據費波那契數列的定義,要知道第 n 項,我們就需要知道第 n-1 項第 n-2 項,於是在程式碼中我們可以看到,在 n 不等於 0 或 1 時,f( ) 函式就會呼叫函式自己,只是這次是想要來計算第 n-1 項與第 n-2 項,所以就呼叫 f(n-1) 與 f(n-2)。

而要如何知道第 n-1 項是多少呢?我們這時就需要知道第 n-2第 n-3 項,如此類推,最後直到我們想要知道的項為第 0 項與第 1 項時,因為這兩項已經被定義了,所以我們就能一一解答前面的所有項了。

還是有點抽象嗎?讓我們以計算 n=5 時的費波那契數列為例,他在進行函式執行時的呼叫順序如下:

下午5.24 2025-3-11的影像
下午5.24 2025-3-11的影像 (1)

  • 費氏數列的時間複雜度 費氏數列的時間複雜度可直接使用觀察法。從上面的可以看出,每往下一層,每一項需要呼叫的次數就變成 2 倍,像 F(5) 要呼叫 F(4) + F(3) 而F(3) 要呼叫 F(2) + F(1) 等等。

而要找到數列的第 5 個數,我們需要的層數是剛好是 5 層,而推展到第 n 個數時,我們剛好需要 n 層。這個部分如果有點抽象的話,可以直接觀察上面的例子,圖中每一層的最左邊那項,是從 F(n) 一路遞減到 F(1),這樣剛剛好會是 n 層。

綜合結論:時間複雜度約等於為 O(2ⁿ) (2 的 n 次方)。

練習題:

  • 利用函式遞迴法計算費氏數列問題
  • 利用函式遞迴法計算最大公因數

亂數

  • 使用 rand 函數產生亂數
  • 要先宣告相關函式
  • #include <stdlib.h> /* 亂數相關函式 */
  • #include <time.h> /* 時間相關函式 */
  • C 語言中若要產生亂數,可以使用 stdlib.h 中的 rand 函式,而在呼叫 rand 函式之前,要先使用 srand 函式設定初始的亂數種子:
    上午9.04 2024-3-13的影像
  • 固定亂數種子:上面的範例中,利用時間產生亂數種子,所以每次產生的亂數都不同,如果需要讓模擬的結果具有可重覆性以便除錯與驗證,這種狀況就必須將亂數種子固定不變,如果沒有固定亂數種子的話,每次跑出來的結果都不同,會讓除錯的難度提高。
    上午9.30 2024-3-13的影像
  • 產生特定範圍整數亂數
    上午9.36 2024-3-13的影像

練習題:

寫一個程式,它可以處理任何筆數的成績,來處理全班分數。

第二個工作:重複輸入,直到輸入「-1」,才停止。 請輸出「輸入的分數筆數」。

輸入範例: 75 75 75 88 70 64 83 89 -1

輸出範例: 8

冒牌費氏數列 令:

  • f(1)=10,f(2)=15,f(3)=16
  • f(n)={f(n-2)+f(n-3),當n>=4

請寫一個程式,輸入n,輸出f(n)值。

輸入範例: 5

輸出範例: 31

C語言程式轉換Java script

基礎C語言練習題

  • 📌 一、河內塔問題介紹: 河內塔問題是:

給定三根柱子與數個不同大小的盤子,要求依照一定規則(每次只移動一個盤子,且大盤子不能放到小盤子上)將所有盤子從起點柱子搬運到目標柱子上。

📌 二、問題特性與時間複雜度: 明確的遞迴解法:

河內塔問題有非常清晰的遞迴(Recursive)解法,可以在每一步遞迴中明確決定下一步該怎麼做。

演算法時間複雜度:

解決 N 個盤子的河內塔,所需步驟為 2ⁿ-1。

雖然看起來是指數成長,但仍屬於確定性演算法,且能夠清晰、直接地計算出每一步。

驗證解法:

不僅可以在多項式時間內驗證答案的正確性,還可以明確快速(多項式時間)地直接得到每一步的搬運順序。

📌 三、為何是 P 問題而非 NP 問題:

項目 河內塔問題特性 是否能有效求解? ✅ 可以快速且明確地找到解法 驗證答案正確性? ✅ 容易驗證(每一步都可以快速確認) 時間複雜度性質 ✅ 指數級但有明確且固定的步驟,屬於可有效計算 由於可有效地用一個確定性演算法解決,因此河內塔問題屬於 P 問題。

NP 問題的特性是可以快速驗證,但不一定能快速解決,典型如旅行推銷員問題(TSP)。

P 問題則是可快速找到有效的解法,典型如河內塔。

📌 四、為什麼河內塔是 P 但複雜度又是指數? 這裡需要釐清一點:

河內塔的最佳步驟數量是指數成長 𝑂(2ⁿ),但這個步驟數量是「已知且確定的」,能用一個非常簡單且明確的演算法(遞迴)精準求出答案,因此依計算複雜度理論的定義,它仍然被歸類在 P 問題的範疇內。

P 問題強調能用確定性的多項式時間演算法去求出解答步驟,而河內塔問題恰巧符合這種明確演算法的條件。

簡言之,河內塔問題雖步驟數指數級,但求解演算法簡明、確定,不具 NP 問題的「不確定性」特性。

  • 「幾A幾B猜數字遊戲」,本質上可以視為一個搜尋問題: 你要從所有可能的數字組合中,透過每次猜測得到的提示(幾A幾B)逐步縮小可能的答案範圍,直到找到正確的數字。

在「複雜度理論」的角度,我們可以這樣判斷:

P 問題(Polynomial time):可以在多項式時間內解出正確答案的問題。

NP 問題(Nondeterministic Polynomial time):如果給你一個「猜測答案」,你能在多項式時間內驗證這個答案對不對,但找到這個答案不一定容易。

幾A幾B猜數字遊戲是屬於哪一種?

答案是:

驗證一個猜測是否正確很快(只要比對兩組數字,看幾A幾B,用 O(n) 時間可以完成),所以這個問題一定屬於 NP。

至於「找到正確答案」這件事,有沒有辦法保證用多項式步驟內完成?

如果答案空間很大(例如猜的是 10 位數、數字可以重複),暴力猜測需要指數級的時間。

但如果題目的設定是小範圍(像猜 3 位數、不重複數字),人或電腦可以在合理步數內解出來。

所以一般來說,完整的「幾A幾B遊戲」屬於 NP 問題,但不一定是 NP-Complete。

如果限制輸入規模(例如最多 4 位數),那這種猜數字遊戲在實務上可以視為 P 問題; 但如果放寬規模(數字任意長),它就屬於 NP,甚至可能趨向 NP-hard。

簡單一句話總結: ➔ 「幾A幾B猜數字遊戲是 NP 問題,不一定是 P 問題。」 (可以快速驗證,但不保證可以快速找到答案,尤其當範圍變大時。)

  • 📌 一、終極密碼猜數字介紹: 通常設定一個介於某範圍內的數字,例如 1~100。

玩家每猜一次數字後,遊戲系統會告知「過大」或「過小」的提示。

玩家根據提示逐步縮小範圍,直到找到正確答案。

📌 二、為什麼是P問題: 有效演算法求解:

有明確且有效的演算法(如二分搜尋法),每次都能將可能的數字範圍縮小一半。

假設數字範圍為 1 到 n,最多只需 log₂n 次即可確定答案,屬於多項式時間內解決(甚至是對數級別,更快於一般多項式級)。

快速驗證:

不論是猜測還是驗證,都可以迅速完成。

📌 三、與NP問題的比較:

特性 終極密碼猜數字(P問題) NP問題(如旅行推銷員) 能否快速解決? ✅ 是 ❌ 不確定,目前未知快速解法 驗證答案是否快速? ✅ 是 ✅ 是 最佳解法 ✅ 有效解法:二分搜尋法 ❌ 一般只能暴力窮舉求解 終極密碼遊戲與NP問題(如背包問題或旅行推銷員問題)本質上不同。NP問題通常沒有已知的有效求解演算法;但終極密碼遊戲則有明確、高效率的求解方法(二分法)。

📌 四、終極密碼遊戲的時間複雜度: 使用最佳策略(二分搜尋)解題: 時間複雜度=O(log₂n ) 屬於非常有效率、快速解決的問題。

🌟 結論: 終極密碼猜數字遊戲屬於P問題,而非NP問題。 因為它具備快速、有效、確定性的求解方法(二分搜尋法),且其複雜度非常低。

  • 旅行推銷員問題(Traveling Salesman Problem, TSP)是一個經典的NP問題。此問題的內容為:一位推銷員必須拜訪若干城市,每個城市僅能造訪一次,並且最終要回到出發城市。問題的目標是在所有可能的路徑中找到一條總路徑長度最短的路線。

TSP被歸為NP問題的主要原因,是因為我們尚未找到一個高效率的、多項式時間(polynomial-time)內求解的演算法。要找到TSP的最佳解,目前最直接的方法便是窮舉所有可能的路徑,計算每條路徑的長度,最後選出最短的路線。然而,對於n個城市,窮舉所有可能的路徑數量為(n-1)!,隨著城市數量增加,運算量呈現指數級成長,這種增長速度非常迅速,以致於在實務中難以有效求解。

儘管求解困難,但若給定一個可能的解答,要驗證這條路徑是否為有效路徑、以及是否滿足特定的總距離限制,卻是相對容易的,可以快速地在多項式時間內完成驗證。因此,TSP符合NP問題的重要特徵,也就是能夠快速驗證答案,但並不確定能否快速求解答案。

另外,TSP問題經典而重要的原因,在於它是「NP完全問題」(NP-complete) 中最具代表性的一種,NP完全問題是指一類問題,只要其中一個問題被證明能夠有效解決,則所有的NP問題都能有效解決。換言之,若有人能找到TSP的快速解法,則所有其他的NP問題也能一併被解決,這也正是電腦科學界至今對此問題投入大量研究心力的主要原因之一。

  • 背包問題(Knapsack Problem)是一個著名的NP問題,在計算機科學、作業研究及組合優化領域中被廣泛研究。基本問題敘述是:有一個背包,其最大承重為W,同時有n個物品,每個物品有自己的重量和價值。目標是在不超過背包最大承重的前提下,選取物品組合,使得總價值最大化。

背包問題被認定為NP問題,主要是因為目前尚未找到一個能在多項式時間內有效解決所有情況的演算法。對於n個物品,每一個物品都有「放入」或「不放入」兩種選擇,因此總共有 2ⁿ 種可能的組合。要找到最佳解,最直接的方法是窮舉所有可能的物品組合,計算其總重量與總價值,再從中選出符合條件且價值最大的組合。由於組合數量隨著物品數量指數級成長,當n很大時,窮舉法變得極度耗時,因此無法在多項式時間內保證找到最佳解。

然而,若有人提供了一個特定的物品選擇方案,我們可以很容易地在多項式時間內驗證它是否合法(是否沒有超過承重且達到特定價值)。這正符合NP問題的定義:雖然不一定能快速找到解,但若有一個解,可以在多項式時間內驗證其正確性。

此外,背包問題有許多變體,如0/1背包問題、分數背包問題等。其中,「0/1背包問題」(每個物品只能選或不選)是典型的NP完全問題(NP-complete),也就是說,它不僅是NP問題,而且是最難的NP問題之一。這代表如果能找到一個多項式時間內解決0/1背包問題的方法,就能解決所有NP問題。

總結來說,背包問題因為符合「難以快速求解,但能快速驗證答案」的特徵,因此被歸類為NP問題,並且在理論計算與實務應用中都具有重要意義。