[Linux Kernel慢慢學]ARRAY_SIZE macro in linux kernel

Posted by John on 2020-03-06
Words 777 and Reading Time 3 Minutes
Viewed Times

前言

為什麼c語言使用function在傳參數的時候,如果傳入一個array會無法正確得到array大小呢?

這其實跟array decay有關,在linux kernel中為了防呆會加上一些code來防止產生這樣的問題(透過在compile time就產生error),接下來從array size該如何取得,到linux kernel的macro來簡單介紹。

array size & sizeof()

在寫c的時候,如果想知道一個array的大小可以這樣寫

1
2
3
4
5
6
int main()
{
int arr[3] = {1,2,3};
printf("%zu\n", sizeof(arr)/sizeof((arr)[0]));
return 0;
}

可是當arr不是一個array,而是一個指向該array的pointer時(pointer to array),就會有問題。由於pointer的大小是4 or 8 bytes(取決於OS是32bit或64bit),所以除以一個array(arr[0])的話就會得到不一樣的結果,例如下面就會得到2而不是3。

1
2
3
4
5
6
7
int main()
{
int arr[3] = {1,2,3};
int *ptr = arr;
printf("%zu\n", sizeof(ptr)/sizeof((ptr)[0]));
return 0;
}

這個問題如果平常沒注意很容易發生,比方說傳了一個pointer進去給function當作參數,在呼叫function時,實際上function的會先創造一個指標變數再去紀錄pointer的內容,所以就會因為上面的理由得到不正確的陣列大小。

在linux kernel內為了有如下的macro(include/linux/kernel.h),定義了如何取得array size:

1
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr))

可以注意到他還有多一個_must_be_array(arr),這是用來確保傳入的arr一定是個array而不是pointer to array,__must_be_array的定義如下:

1
2
/* &a[0] degrades to a pointer: a different type from an array */
#define __must_be_array(a) BUILD_BUG_ON_ZERO(__same_type((a), &(a)[0]))

先來看_same_type在幹嘛再回來理解這一句:

1
#define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))

__builtin_types_compatible_p是gcc的extension,有興趣的請再自己往下追,總之它會比較兩個variable type,如果相同回傳1,不同則回傳0。

所以__must_be_array就是比較a和&(a)[0]的type是不是相同:

  • 如果a是array,&(a)[0]會被degrade成pointer,_same_type會回傳1
  • 如果a是pointer,&(a)[0]仍然是一個pointer,_same_type會回傳0

接下來來看BUILD_BUG_ON_ZERO在幹嘛:

1
#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))

  1. 對e做兩次not,確保e一定會是0或1
  2. 然後乘上-1,確保e一定會是0或-1
  3. 透過bit-field的技巧宣告一個包含int的struct,詳細可以參考超連結

當e不是0的時候,這一個macro就會產生一個包含-1個bit的struct,不過沒有這種東西,所以在compile time就會產生錯誤。

好了,追了那麼多code,回頭重看一次,在linux kernel的ARRAY_SIZE macro為了預防傳入的變數是pointer而不是真正的array,背後其實花了很多心力來防範這件事,使得如果發生問題就會在編譯時期就失敗,避免了因為誤用而在run-time才發生問題的狀況。

最近開始慢慢接觸linux code,真的覺得越來越不懂c了呢。

Reference


>