August 2, 2017

[CS50] Week 1 C - 中文導讀筆記

*此筆記是依據胡立老師的 CS50 中文導讀直播影片所寫。

胡立老師的直播:
CS50 中文導讀:week 1

如果想要對第一週內容有更全面的了解,建議配合 [CS50] Week 1 C - 課程筆記 一起閱讀。因為第一週課程筆記內容已經蠻長了,所以我把課程筆記裡沒寫到,而胡立老師有額外補充的部分寫在這一篇,如果在課程筆記就寫過的部分(像是 command line 指令),這篇就沒有重複寫。這篇我略過關於 Scratch 的講解。

以下是我的筆記整理:

1. continue;break; 使用方法/時機。
break; 在條件判斷式 switch 裡面蠻常見的,用來跳出一個段落 (block)。但是 continue; 我就比較不熟。

胡立老師在直播裡用 while 迴圈跟 if 示範 continue;

輸出結果:

while 迴圈跑到 count == 5 的時候,會進入 if 那個段落,遇到 continue; 後,再次回到 while 迴圈開始的地方,然後還是 count == 5,所以又再次進入 if 那個段落,遇到 continue;,又回到 while 迴圈開始的地方,於是就一直重複而無法跳到下面的 count++;,所以輸出結果只有 1、2、3、4。

移動 count++; 的位置後:

輸出結果:

count++; 移到上面位置後,count 進入 while 迴圈就先加 1,所以輸出結果的開始數字變成 2,到數字等於 5 的時候沒有被印出來(因為遇到 continue; 就跳回 while 迴圈開始的地方),然後印出 6、7、8 以及後面的數字。

break; 的示範:

輸出結果:

break; 執行後就跳出 while 迴圈,所以輸出結果只有 2、3、4。

2. 在 CS50 IDE 上有內建 C 的編譯器 (compiler),編譯器的功能是把人類打的原始碼 (source code) 轉換成電腦看得懂的機器語言 (machine code),看似簡單的編譯過程事實上分成四個階段:

1) 預處理 (preprocessing)
a. 檢查語法錯誤。
b. 處理 #include。找到你 #include 的檔案,然後貼到你正在編輯的檔案上。
c. 可以輸入以下指定來看 preprocessing 在做什麼事情(以 hello.c 為範例):
clang -E hello.c

輸入以上指令後,可以看到 #include <stdio.h> 的內容被貼到檔案裡,原本 hello.c 檔案內的註解也消失了。

2) 編譯 (compiling)
a. 把 C 的程式碼轉換成組合語言,組合語言是比較低階的語言 (C code -> Assembly code)。
b. 輸入以下指定來看 compiling 在做什麼事情(以 hello.c 為範例):
clang -S hello.c

輸入以上指令後,會出現一些看不懂的文字(比 C 語言難懂,比機器語言好懂)。因為直接把 C 語言編譯成機器語言比較難,所以會先把 C 語言編譯成組合語言。

3) 組合 (assembling)
把組合語言轉成機器語言 (Assembly code -> Machine code)。

4) 連結 (linking)
a. 把你寫的程式碼,系統的程式碼,CS50 的程式碼,全部連結組合在一起。
b. 輸入以下指定來看 linking 在做什麼事情(以 hello.c 為範例):
xxd -l 40 -c 10 hello

輸入以上指令後,把 hello.c 的 binary 檔案內容顯示出來(事實上是用 16 進位顯示出來)。
xxd 是指令。
-l 是 limit 的意思,表示顯示出的資料限制。
40 顯示 40 bytes 的意思,表示顯示出 40 bytes 的資料。
-c 是 column 的意思,表示一行顯示幾個 bytes 的資料。
10 是 bytes 的意思,表示一行會顯示 10 bytes 的資料。

c. 修改 binary。
輸入以下指令後可以修改原本的檔案(以 hello.c 為範例):
vim -b hello

輸入以上指令後,會進入另一個畫面,輸入 %!xxd 可以找到自己原始碼中的字母對應到的數字,修改數字後輸入 %!xxd -r 再輸入 :wq 存檔離開後,執行 ./hello 會發現原始碼被修改了。(參考:在 Linux 下使用 vim 配合 xxd 查看并编辑二进制文件

3. 數字的儲存(假設數字只有 4 bits 大小)。
1) 如果假設數字只有 4 bits 大小,那麼我們可以用二進制 0000 ~ 1111 來表示整數:

但這樣子就只有正整數,無法表示負數了。

2) 負數的表示。
a. 直覺用第一個 bit 表示負值 ,但會造成 +1 跟 -1 相加不為 0:

第一個 bit 以 0 表示正數 +,1 表示負數 -。
但只是改變第一個 bit,會發現相加應該為 0 的正負數,相加後卻不等於 0。

b. 所以利用結果來反推答案,利用溢位 (overflow) 來表示 0(實際上是10000):


這裡反推出二進制的 0001 跟 1111 相加會變成 10000,但因為只能儲存 4 bits 大小的數字,所以數字溢位後變成二進制的 0000,也就是 0。

由剛才的例子發現,利用數字溢位 10000 可以表示 0,但要考慮哪兩數相加為 10000 之前,可以先拆解成更小的步驟:

i) 先考慮如何讓兩數相加為 1111。雖然我們目標是要找兩數相加為 10000,但找到相加為 1111 比起要找到溢位後成 10000 比較容易,因為不用另外思考進位問題。這裡用二進制 0111(十進制 +7)為例:


0111 與 1000 相加會成為 1111,但還不是我們要找的答案。

ii) 把剛才找出的答案後再 +1。

把剛才找到的 1000 加上 1 之後就變成 1001,而最後的答案也成為 10000,如我們一開始的目標一樣。


之後就反推二進制 1001 是十進制的 -7。

iii) 用以上的方法推論出數字只有 4 bits 大小的情況下,可以表示哪些正負整數:

然後就會發現:如果現在有一個數字是正數,把所有 bit 反轉之後 +1,那個數字就是負數。
而 1000 是如何得出它是 -8 的呢?1000 反轉之後 +1 還是 1000,所以由 1001 - 1 得出 1000 是 -8,因為 1001 是 -7,所以 -7 -1 = -8。
0 雖不是正數或負數,不過會佔一個 bit,所以正整數會比負整數少一個。

就可以得到以下的結論:

在 C 語言裡 int有 32 bits,所以最大的正整數是 2^31 - 1 (2147483647)。

3) 小數的表示方式。
a. 最大問題:用有限(電腦記憶體有限)表示無限,所以怎麼存都不可能存的精準。
b. IEEE 754 是二進位浮點數算術標準,請參考維基百科:IEEE 754 Wikipedia
c. 十進位小數轉換成二進位,請參考:10 進位轉 2 進位小數算法。有些十進位小數轉換二進位的時候會有無法轉換完全的情況(像是十進位的 0.3),所以會有不精準的情況產生。


這次的導讀筆記就到這裡,許多內容都從胡立老師的投影片截圖下來的。筆記如果有錯還請指正。


No comments:

Post a Comment