1
考慮以下程式碼:
在 gcc 編譯時指定 -std=c99
(參照 C99 規範) 輸出為以下兩行:
從以下選項中挑出最接近的描述。
注意:同樣的程式碼經過 C 和 C++ 編譯器產生的執行檔有截然不同的輸出
作答區
A = ?
(a)
這程式之所以能夠通過編譯,是因為 GNU extensions;(b)
C99/C11 規格書對此已有正面描述,執行結果符合預期;(c)
會有上述執行結果,顯示 gcc 編譯器內部的實作錯誤;延伸題目:
-pedantic
2
考慮以下程式碼:
這段程式碼在 macOS 和 GNU/Linux 有著不同的執行輸出:
從以下選項中挑出最接近的描述
作答區
B = ?
(a)
純粹只是 C 編譯器行為的落差,實務上不需特別留意;(b)
char a
和 char b
這兩個物件的生命週期僅分別在第 11 行和第 16 行有效,一旦超出 scope (作用範圍),C 語言編譯器就會主動釋放物件,而透過 &
(value-of) 運算子得到的地址自然就不再有效;(c)
char a
和 char b
這兩個物件的生命週期僅分別在第 11 行和第 16 行有效,一旦超出 scope (作用範圍) 仍操作物件的話,會導致 undefined behaviour;(d)
gcc 的實作不符合 C99/C11 規格描述;提示:
6.2.4:2
節提及物件的生命週期:The lifetime of an object is the portion of program execution during which storage is guaranteed to be reserved for it. An object exists, has a constant address, and retains its last-stored value throughout its lifetime. If an object is referred to outside of its lifetime, the behavior is undefined. The value of a pointer becomes indeterminate when the object it points to (or just past) reaches the end of its lifetime.
延伸問題:
PRIxPTR
為何?在哪裡定義?3
考慮以下 cmov (contional move) 指令的 C 程式實作:
在 Intel Pentium Pro (也稱為 P6 微架構) 後引入新指令 cmoveq
,於是上面的 C 程式等價於以下 x86_64 組合語言輸出:
關鍵不僅在於程式碼的密度 (code density),更在於少了分支,這對於密碼學的實作非常關鍵,依據 cryptocoding.net 的建議 Avoid branchings controlled by secret data,這可避免 timing attack。請改寫上述 C 程式,使其得以在常數時間內完成 cmoveq 等價功能。欲改寫的新程式碼如下:
請補完上述 C1
和 C2
程式碼。
作答區
C1 = ?
(a)
p1 | mask(b)
p1 ^ mask(c)
p1 & mask(d)
p1 | ~mask(e)
p1 ^ ~mask(f)
p1 & ~maskC2 = ?
(a)
p2 | mask(b)
p2 ^ mask(c)
p2 & mask(d)
p2 | ~mask(e)
p2 ^ ~mask(f)
p2 & ~mask參考資料:
現在的編譯器最佳化已能夠分析特定程式模式,並以硬體指令替代,如 clang/llvm 可將上述 cmov 程式轉為對應包含 cmov 指令的組合語言,實驗方式為:
檢查編譯器產生的 cmov.s
,確認裡頭包含 cmoveq
指令
延伸題目: