關於 C 與 Python
弱型別 & 強型別
Python 是弱型別語言,變數的型別在執行時才決定。所以你可以反覆橫跳也不會報錯:
1 | x = 10 # x 是整數 |
但強型別的 C 就不行了,你要先宣告。
然而弱型別會讓效率差一點。你變數換來換去,表面上很方便,其實底層依舊有在幫你找新的地址來存東西,只是你不用自己操心。
為什麼 C 至今依舊不敗?就是因為底層的東西我們自己操心,自己操作,雖然難寫,但可控性更高。
Python 自有一套記憶體管理術
比如說:
1 | >>> a = "hey" |
當我賦值 b 跟 a 一樣,Python 就指向同一個位置,很省哦。但 C 語言中,就算兩個變數的賦值一樣,依然會分別存在兩個不同的位置。
好幾步之後,我們突然賦值 c (此處指上述程式碼中的變數c,不是C語言)為 “hey”,Python 還記得這個 “hey” 其實前面有人用過,反正記憶體也還沒把這個資料洗掉(還沒內存回收),你 c 就繼續用這個位置吧!可見,Python 自有一套記憶體管理術,而我們使用者也不用太操心這些環節。
Character & String
C 內建字元型別 char,如果你不引用外部的 library(例如 string.h),就只能用 char 來拼湊字串。這很合理,字串是人類在看的,咱 C 是面向硬體的。
Python 完全相反,沒有字元型別,但原生自帶字串型別 str。這很合理,Python 是面向咱人類的。
Interpreter & Compiler
Python 是用 interpreter 直譯的,一句一句執行。
C 是用 compiler 編譯成執行檔才能執行。
遊戲引擎、嵌入式系統
很多遊戲引擎都是用C語言開發的,虛幻引擎、Unity就是用C++。
據悉(C 的老師說的),後續的效能優化是對組合語言慢慢刪減調整。
嵌入式系統也常用C開發,同樣的理由,因為較為底層,所以輕量且高效。
編譯順序
C的編譯器是按照順序編譯的。
函數(function)要寫在主函數(main)的前面,若是反過來,編譯器會不知道你在調用什麼。
printf 的處理順序
1 | int i = 6; |
printf 位於 <stdio.h> 庫中。這段程式會輸出 8,7,6
。
p.s. i++
是「後置遞增」,表示在當前語句完成後,i
的值會增加;++i
則是「前置遞增」,先增加再賦值。
至於原因,跟編譯器的處理方式有關,三個變數從左到右被放到 stack 裡面,所以最後出來的順序是反過來。如果遇到 i++ 就先賦值了,而如果遇到 ++i 則運算完後把指標指向目前的 i。
然而這句包含未定義的行為,很危險,我們應該避免同一句裡面對同一個參數做多次修改。我們如果改成這樣:
1 | int i = 6; |
如此就會輸出 6,6,7
。
但如果:
1 | int i = 6; |
你猜猜看會輸出什麼?輸出了 8,8,8
,我暈。
算數運算子
C 跟 Python 裡面都有基本的 a+b, a-b, a*b, a/b, a%b
,但其中 a/b
裡面 C 只給整數商,Python 會直接浮點送你小數點。
那 Python 要怎麼只拿整數商?必須寫作 a//b
才行。
另外 a**b
的指數運算 Python 有但 C 沒有。
算數賦值運算子
a = a + 5中的「+」就是算術運算子,「=」就是賦值運算子。
來咯,C++ 跟 Python 裡面都有這兩者的合體——「算數賦值運算子」。
剛剛的 a = a + 5
用算數指定運算子簡化成 a += 5
。總之就是,先進行運算,接著直接賦值成運算後的結果。
return 0
C++裡面常看到return 0:
1 | int main() |
函數執行完一定要返回一個0嗎?嗯,收到0代表這個函數執行完畢。
而且其實就算你忘了 return 0,現在編譯器也會自己幫你 return 0。
布林函數
C 沒有原生的布林函數,也就是他不認識 true 跟 false。但可以這樣玩:
1 | if(0.1) |
會 print 出來。
三元運算子 (ternary operator)
1 | printf("%d",(0)?1:2); |
這裡因為括弧內為假,所以最後輸出「2」。
設條件居然還能不寫 if!C 真的很喜歡簡潔,但同時閱讀起來很不直觀。
喔其實 python 也有三元運算:
1 | print("true" if 1 else "false") |
邏輯運算子
Python 很貼心直接用自然語言的 and, or, not,而 C 分別是 &&
, ||
, !
。
位元運算子
C 底層到什麼程度呢?可以對位元進行操作。
1 | printf("%d",13&7); |
最後會得到「5」,因為我們對 13 跟 7 進行二進位的 AND 操作。
其他的還有 |
for OR, ~
for not, ^
for XOR。
另外還能 <<n
就是左移位數(低位補零),其實就是乘以 2^n。反之就是 <<n
。
Python 當然就沒有這些玩法了。
Slicing
Python 獨有的 Slicing:
1 | b = "Python rules!" |
其中語法為 sequence[start:stop:step]
。如果頭尾省略不寫的話,如果 step 為正,那就是開頭當做 start;若 step 為負,結尾當做 start。(如果 step 不寫的話,默認是 1)
居然在同一個語法自帶 if 判斷?好吧。
物件導向 Object-Oriented
1 | b = "Python" |
作為物件導向的語言,在你創建了 b="Python"
之後,Python 就自己幫你維護了關於 b 這個物件的各種資訊跟方法。
例如 len(b)
是 b 這個 string 的長度。你有自己寫程式去進行判斷嗎?沒有,但這件事 Python 幫你維護好了。
例如 b.upper()
就是一個方法,只要你是 string,都可以調用這個方法來將字串裡面的字都轉成大寫。
1 | print("XD"*10) #輸出 XDXDXDXDXDXDXDXDXDXD |
你看連 string 的乘法都可以。
容器們:Tuple, List, Dictionary, Set
這些資料結構只有 Python 有。
特性 | tuple |
list |
---|---|---|
可變性 | 不可變 | 可變 |
語法 | 小括號 () |
方括號 [] |
方法數量 | 僅有 count 和 index |
多數據操作方法(如 append ) |
性能 | 更高效(因為不可變性) | 相對低效 |
使用場景 | 用於靜態數據 | 用於動態數據 |
注意 tuple 如果只有一個元素時,必須加逗號:single_element_tuple = (1,)
解包 Unpacking
1 | '1','a'),('2','b'),('3','c')) tu = (( |
只要結構上大家能一一對應,通常 python 都能幫你兜在一起。
f-string
1 | "Shuai" name= |
很方便餒! 順帶一提,此例中的句子都是真人真事。
1 | 3.1415926 pi= |
當然啦,指定格式也是可以的。
1 | for i in range(5): |
python 預設的 print 會幫你每次都換行(也就是 \n),怎麼自定義?用 f-string 可以指定 print 的結尾是什麼!
偷偷幫你換行
C 裡面你通常要自己透過 \n
來換行,不然大家就擠在同一行。
Python 不出意外地(?)很自動就幫你換行了,十分方便。但如果你今天不想換行的話,要特別跟他說一下,例如:
1 | print('hi'); print("hello") |
字元轉整數(char to int)
1 | int a = '9'-'0'; |
這行是啥?其實就是 a=9,酷吧!不會報錯哦。
elif
大部分程式語言跟 C 一樣用 else if
作為條件判斷的語句。
就 python 跟大家不一樣用的是 elif
。
檢查是否完整執行 loop
1 | for i in range(10): |
python 還拿了 else
來判斷 loop 是否有被 break
。
C 沒有類似的原生設計。
函數註解
1 | def hello(): |
C 有這種事嗎?沒有。