在C語言程序開發中,有經驗的程式設計師有時會定義只有一個數組成員的結構體,雖然語法簡單,但是卻常常讓初學者感到迷惑:這麼做有什麼好處嗎?
struct ABC {
unsigned long array[MAX];
} abc;
答案是肯定的,這樣做主要有兩個好處:一是便於值傳遞,二是便於後期擴展。
方便的數組值傳遞
看過我之前文章的讀者應該明白,調用C語言函數時,如果將數組作為參數傳遞給函數,那麼在被調用函數內部,數組常常會退化成指針。下面是一段C語言代碼示例:
#include <stdio.h>
void fun(char arr[16]){
printf("sizeof arr is %d\n", (int)sizeof(arr));
}
int main(){
char a[16] = {0};
fun(a);
return 0;
}
編譯並執行上述C語言代碼,得到如下輸出:
# gcc t.c
# ./a.out
sizeof arr is 8
可見,在函數 fun() 內部,sizeof(arr) 並不等於數組長度 16,而是等於 8(指針長度,我的機器是 64 位的,指針佔用內存空間為 8 個字節),這說明即使函數 fun() 的參數C語言代碼明確指定為 fun(char arr[16]),在函數內部,arr 還是退化成指針了。
而如果將數組封裝在結構體內部,將結構體作為參數,那麼在函數內部,我們依然可以獲得完整的數組,請看下面的C語言代碼示例:
#include <stdio.h>
typedef struct{
char arr[16];
}String;
void fun(String *str){
printf("sizeof arr is %d\n", (int)sizeof(str->arr));
}
int main(){
String s;
fun(&s);
return 0;
}
編譯並執行上述C語言代碼,得到如下輸出:
# gcc t.c
# ./a.out
sizeof arr is 16
一切與預期一致。上面的C語言代碼有意將結構體 typedef 為 String,因為它的行為很像用於描述字符串的「新類型」。讀者應該明白,C語言中的數組是不支持直接賦值的:
char a[16], b[16];
...
a = b; // 非法
但是藉助於C語言的結構體語法,String 類型是可以直接賦值的,這使得編寫代碼方便不少:
String a, b = {1,2,3,4,5};
a = b; // 合法
printf("%d %d %d %d %d\n",
a.arr[0], a.arr[1],
a.arr[2], a.arr[3], a.arr[4]);
// 輸出 1 2 3 4 5
方便後續擴展
C語言中的結構體可用於將一些基本類型的數據封裝成一個具有內在聯繫的數據結構,而且結構體並不限制自身成員的數目和佔用內存空間的大小,這樣的特性使得在C語言項目後續開發中添加數據方便不少。
例如,可能剛開始 fun() 方法需要完成的需求比較簡單,可能它只需要接收一個數組就可以:
void fun(char arr[]);
char a[16];
...
fun(a);
隨著項目的繼續推進,我們發現 fun() 函數不僅需要數組 arr 的地址,還需要知道 arr 中究竟有多少有用的數據,因此需要對 fun() 函數做如下修改:
void fun(char arr[], int size);
char a[16];
...
fun(a, size);
顯然,如果一開始 fun() 函數接收到的參數是裸數組,那麼在後續開發中,要擴展參數就必須修改 fun() 函數原型,相應的調用處也需要修改,如果 fun() 函數的調用處特別多,那麼擴展工作將會無比痛苦,並且很容易出錯。
另外一個問題是,void fun(char arr[], int size); 中的 arr 和 size 是對應關係,但是卻沒有語法規則約束這種對應關係,因此在複雜的項目開發中,將數組 b 的 size 誤認為是數組 a 的 size 使用的情況是極有可能存在的,這樣的錯誤發現和排查都相當困難。
如果,我們一開始就將數組封裝在結構體裡,在創建 fun() 函數時,其實並未增加多少工作量:
typedef struct{
char arr[16];
}String;
void fun(String *a);
String a;
...
fun(&a);
現在需要對 fun() 方法擴展,增加 size 參數:
typedef struct{
char arr[16];
int size;
}String;
void fun(String *a);
String a;
...
fun(&a);
顯然,唯一要做的就是為結構體 String 新增了一個 size 成員以及相應的方法實現,fun() 函數原型不動,因此所有調用 fun() 函數的代碼也就不需要修改了,這樣的擴展無疑是方便的。另外,size 和 arr 的對應關係由C語言結構體語法約束,這也在最大程度上避免了程式設計師錯用 size。
小結
C語言語法本身是非常簡單的,代碼本身是易懂的,但是簡單的組合背後常常隱藏著程式設計師匠心的一面,這些組合常常能為項目開發帶來意想不到的好處。
歡迎在評論區一起討論,質疑。文章都是手打原創,每天最淺顯的介紹C語言、linux等嵌入式開發,喜歡我的文章就關注一波吧,可以看到最新更新和之前的文章哦。
未經許可,禁止轉載。