19.%d C — Accounting Review

春麗 S.T.E.M.
7 min readAug 28, 2021

--

目錄

⦿ C語言的使用方法
⦿ 最大值與平均數
⦿ 程式碼拆分
⦿ 函式宣告
⦿ 物件導向的思考

C 語言的使用方法

在 C 語言中,要提示使用者輸入數字,使用方法如下:

在 Xcode Playground 中,#include <stdio.h> 以導入 C 語言的框架,在 C 語言中是以 int main 做為開頭,此為一個帶有參數的函數,int argcconst char * argv[] 即是參數的宣告,一個是 int 型別,另一個則是 char 型別,並且為 const。

int i, n, Max, Avg = 0; 為變數宣告及初始化; 作為每段的結尾,與前後的程式碼區隔開來。

以迴圈來提示使用者輸入十個數字,for(i = 1; i ≤ 10; i++) { } 表示 i 從 1 開始,i++ 表示 i 遞增,i ≤ 10 表示迴圈從第 1 次開始跑到第 10 次,即是共進來此迴圈十次。中間一樣用 ; 隔開。

printf、scanf 表示每次印出都換行;每次輸入(Enter、Return)後都換行。printf(" 第 % 個 ", i) 這段程式碼表示印出第 i 個,由 %(precent sign)對應到未知數 i,若 i 為 2 則印出 第 2 個

scanf(" %d ", &n) 這段程式碼表示掃描/讀取使用者的輸入,一樣由 % 對應到 &n,&n 的 &(Ampersand)為取址符號,表示使用者輸入時要給予這個變數 n 一個記憶體空間,而 d 更限定了使用者輸入的是整數。

兩段程式碼合一起看即為下面這個結果:

詢問使用者第一次到第十次,使用者輸入 20、30、40⋯⋯最後輸入 10,來取最大值平均

繼續閱讀|回目錄

最大值與平均數

條件敘述句中,if (i == 1 || n > Max) { Max = n } 表示前一段的迴圈,第一次進來時,便將 Max 令為 n,n 即是使用者輸入的變數,同樣地,我們在前一段給予它一個記憶體空去儲存(&n),由於 n 是一次次輸入的數字,在第一次必須先將之令為 Max,才能在第二次輸入時去比較,即是第一次 Max 為 20,第二次的 n 為 30,但 n > Max,所以 Max = n(30)。

在其後的是 Avg += n 表示每次輸入進來的 n 要相加,這種寫法在迴圈裡即是把所有的值相加,所以你常會看到 sum = sum + i 或 sum += i,因為我們不需要兩個變數來分別儲存 sum 與 Avg,所以這段用 Avg += n 即可。

而跳出迴圈後,Avg = Avg / 10,才得到真正的 Avg(平均數)。

再說說 if (n > Max) { Max = n } 這種條件敘述句,Max 已經是第一個使用者輸入的數字,若接著輸入的數字有比較大,Max 才令為這個數。

在程式語言中,可稱之為打擂台,看到下方例子:

int a, b, c, d, e, f, g, h, i, j, Max = a;

if (b > Max) {
Max = b;
}
if (c > Max) {
Max = c;
}
⋯⋯

先是宣告了 a、b、c⋯⋯j 這些變數,並將 Max 令為 a,接著開始比較所有的變數的大小,由於他們的型別為 int,所以當然可以比大小

若是 b > Max,Max = b;若是 c > Max,Max = c⋯⋯。

當然,這種方式寫出來的程式碼相當易讀,不過同樣的事情一直重複程式碼去做就相當不符合效益。

雖然我們用的是 if (i == 1 || n > Max) { Max = n } 來去做判斷,其實也只會在 i == 1,即第一次輸入的數字進到迴圈,但看到下面:

 if (i == 1) {
Max = n;
} else if (n > Max) {
Max = n;
}

這段程式碼跟原先的程式碼並沒有什麼不同,但每次你都會判斷 i 是否等於 1,這是不是也不符合效益呢?

然而更有趣的是,如果使用者第一次輸入的是大於 0 的數,那其實只需判斷 if (n > Max) { Max = n } 也同樣成立,就不用加入 if (i == 1) 了。

下面,我們來想想看有沒有將程式碼優化的方法。

繼續閱讀|回目錄

程式碼拆分

若將原先的函式抽離,改成如下:

先看到 int main 裡,int n[10] 表示宣告了整數陣列,n[10] 意思是說給 n[] 這個陣列有十個空位,所以 10 就是它的大小了。

可以用它來儲存 10 個數字。

迴圈沒有變,i 從 1 ~ 10,printf 一樣沒有變,但 n 已宣告,所以在 scanf 裡要設值進去需寫成 &n[i-1],為什麼呢?

陣列設值的方式為 &n[0]、&n[1]、&n[2]、&n[3]、&n[4]、&n[5]、&n[6]、&n[7]、&n[8]、&n[9],所以 0 ~ 9 用來表示陣列的位置,雖然它的大小是 10,但第一位是 n[0],最後一位是 n[9]

看到了嗎?迴圈裡並沒有計算最大值與平均值喔!因為我們將程式碼拆了出來。

函式宣告

C 語言中,如果要自己寫一個函式,我們會在 int main 的前面去寫上一個空的函式,如同前段int max(int n[10])int avg(int n[10]),這種宣告方式是不是很像 int main(int argc, const char * argv[]) 呢?

是的。

然而,函式的撰寫我們會擺在 int main 的後面,如下:

max 為找十個數的最大值的函式,先令最大值為第一個數,即是 int Max = n[0]。

接著看到迴圈,這十次迴圈裡,若 n[i] > Max,注意看!迴圈寫錯了呢,因為 for (int i = 0; i < 10; i++) 同樣意思的迴圈為 for (int i = 1; i ≤ 10; i++) 才對,所以這邊其實是 i = 0。

若 n[0] > Max,Max = n[0],但 Max 已經令為 n[0] 了就不會再令一次。

最後 return 的是 Max。

我們再看到 int avg(int n[10]) 裡,Avg 這個 int 型別的變數先令為 0,在這十次迴圈裡,同樣是累加所有的數,故 Avg += n[i],最後除以總數取平均,即是 Avg = Avg / 10。

調用此兩函式,是在 int main 裡如下操作:

這即是說,我把 n[10] 中最大的數值找出來,也把 n[10] 中所有數值加起來除以總數(取平均),代入時,只要將 n 傳遞至兩函式作為參數即可。

最終結果自然沒變,如下:

繼續閱讀|回目錄

物件導向的思考

我們想想物件導向的封裝(encapsulation),封裝常是讓你不知道內裡有什麼就能使用它,複雜的事在內裡已處理完成,工程師負責調用即可。

就像調用求最大值的函式,我們不需知道函式的內容,只需知道怎麼用;將麻煩的東西移出去,不要跟呈現給使用者的文字混在一起。

如此程式碼才會呈現易讀、好維護的姿態。

就像在 Xcode 中,我們將 Controller 的權責分配出去,例如拆成 event trigger、API calling⋯⋯等。

不要把所有複雜的函式都寫在 Controller 裡,程式碼就會變得易讀好維護,除此之外,代碼的複用也很重要,在這次文章裡,我們總不會希望當整數陣列變大的時候,也要重寫一個函式。

因為目前的 int max、int avg 只滿足 n[10] 作為參數,所以我們會從 n[j] 開始去想,在你真正要使用這個函式時,j 才給定了 n 這個 int 矩陣大小,我們略過實際操作,是提供想法。

接著再往下推,也許我們希望變成 float、double 型別也能代入函式,甚至是特定的 string 型別,例如寫成數字的 string 也能通過使用同一個函式幫助我們找到 MaxAvg

講到這裡,是否令你想起 swift 的泛型呢?是吧。

這次就分享到這,感謝您的閱讀。

繼續閱讀|回目錄

--

--

春麗 S.T.E.M.
春麗 S.T.E.M.

Written by 春麗 S.T.E.M.

Do not go gentle into that good night, Old age should burn and rave at close of day; Rage, rage, against the dying of the light.

No responses yet