contributed by < uccuser159
>
align4
巨集的作用是,針對 4-byte alignment,找到給定地址的 round up alignment address。預期程式輸出 align4(p) is 00001998
K
= ?
來看 #define align4(x) (((x) + K) & (-4))
此處的K
即是除以4的最大餘數。將 K
加上去後可以讓 x 往上進位到該有的4的倍數同時不要讓要 x 超過4的下一個倍數,所以 K
值應該為3。
Reference
原始程式碼 ( line 33 ~ line 37 ):
__ALIGN_KERNEL & __ALIGN_KERNEL_MASK ( line 10 & line 11 )
應用 ( line 35 ):
為確保堆疊的指標所指向的位址是正確的
缺乏實際 Linux 核心原始程式碼及其「應用場景」的解說
jserv
為了提高存取效率,例如記憶體存取、分頁,需要將資料對齊到 2N ( N 為非負整數 )。
而編譯器在分配記憶體時,會按照宣告的型態去做對齊。
程式執行結果:
typeof(x)
表示取 x
的類型,此例的ALIGN(x,a)
中 x
是 int ,則typeof(x)
為 int ,故 (typeof(x))(a)-1
,表示把 a 轉化為 x 的類型,再減1,當作mask
。以ALIGN(1,4)
來說,mask=0000...00011(32bits)
,& ~(mask)
即是在做 round up 成4的倍數。
For example, the “maximum” function is commonly defined as a macro in standard C as follows:
But this definition computes either a or b twice, with bad results if the operand has side effects. In GNU C, if you know the type of the operands (here taken as int), you can avoid this problem by defining the macro as follows:
Here is how the two together can be used to define a safe “maximum” macro which operates on any arithmetic type and evaluates each of its arguments exactly once:
TODO: typeof
是 GNU extension,找出 GCC Manual 對應描述並解說為何 巨集定義用得到此 extension
jserv
Reference
可改寫為以下等價的程式碼:
請補完程式。
先將func
函式的參數從1(00012)代到8(10002):
工程人員說話應該精確,英文用語怎麼寫,就用對應的中文陳述
jserv
執行結果:
由結果推論是在判斷一數字 x 是否為2的次方。
return x && ((x & (~x+1))==x)
先來嘗試 x & (~x+1)
x = 1 (00012) :
x = 2 (00102) :
x = 3 (00112) :
x = 6 (01102) :
x = 7 (01112) :
x = 8 (10002) :
發現x & (~x+1)
的效果為將數字最右邊 bit 的1保留,其餘 bit 為0。
所以符合x & (~x+1)==x
式子的數字 x 應該要是化成二進制時只有其中一個 bit 為1,即2的次方(2n) (例如: x=01002 . 00102 …等等)。
而x && (x & (~x+1)==x)
即是在 x=0 的狀況下回傳 False,將其狀況排除。
Z 值就像一個在LSB檢查"1"的偵測器,檢查 x 的 LSB 是否為1,否則將 x 做右移運算。
有此可知,Z 值應該為1。
while(x && unknown <= 1)
會將 x=0 的狀況排除且unknown拿來記錄"1"的個數,當在 if 條件中檢測到"1"即將 unknown 加1,這邊舉三種例子:
例1: x=00002
例2: x=00102
例3: x=01102
此等價的函式只想在 x 中找僅有1個bit為"1"的數,所以回傳的布林判斷條件為unknown == 1
判斷一整數為正 or 負 or 0
判斷一整數為非負整數
此方法發現與結果不合,問題出現在數值小於0的狀況。
原因是當有號數在做右移運算時,會使用正負號位元填補空出的位元位置,所以小於0的數,經過右移31bits後為1111…1112(32bits),而非0000…0012(32bits)
故將程式改成:
Reference
能否編譯並執行呢?若可,為什麼呢?
Why?
先了解 C99 規格:
A function designator is an expression that has function type. Except when it is the operand of the sizeof operator or the unary & operator, a function designator with type ‘‘function returning type’’ is converted to an expression that has type ‘‘pointer to function returning type’’.
The unary * operator denotes indirection. If the operand points to a function, the result is a function designator
例如:
A "function designator" is converted to a "pointer to function returning type"
=> test1 是一個 function designator 所以它會轉換成一個 pointer to function
If the " * " operand points to a function, the result is a function designator
=> (*fptr1) 的結果會是一個 function designator
從例子可以得知,只要 * operator 指向一函式,無論 * operater 有幾個最終出來的結果都會是一個function designator,而 function designator 會再轉換成一個 pointer to function,最終指向 test1 此函式。
先將(*******puts)
可看作 (*(*(*(*(*(*(*puts)))))))
根據剛剛的結論:不管有幾個 * operater,最終出來的結果都會是一個function designator,而 function designator 會再轉換成一個 pointer to function,最終還是指向 puts 函式。
puts
的 function prototype 是 int puts(const char *s)
最終主程式 main
將呼叫 puts
函式 print 出 "Hello"
Reference