作者FRAXIS (喔喔)
看板C_and_CPP
標題[心得] Compound literals, flexible array members
時間Sun Apr 11 22:40:48 2010
下面是我以前學C99新功能的心得,跟大家分享。
假設有一個struct S有兩個欄位x和y,在ANSI C年代,如果要把x, y設定值
只能這樣寫
A.x = 1;
A.y = 1;
在C99則允許這樣
struct S A = (struct S) {1, 1};
方便很多,這功能稱為compound literals
如果要傳參數給function,也可以這樣寫f( (struct S){1, 1} );
甚至還可以對他取位址
struct S *p = &(struct S) {1, 1}; // 取位址
甚至連原始型態都可以取位址
int *p = &(int) {1}; // p = &1不合法..
可以想像compound literal是一個匿名的變數,但是他跟string literal又稍有不同
char *p = "Hello";
p[1] = 'a'; // 會出錯 因為string literal是const
然而
struct S *p = &(struct S) {1, 1};
*p = (struct S) {0, 0}; // 合法
不過當匿名的變數是在函式裡面宣告的,他的生命週期就跟區域變數一樣
int *f(void) { return &(int) {5}; } // 會出錯
可惜他跟vla不能混用
f( (int [n]) {5} ); // 不合法
C99之中可以指明只對某個欄位初始化,這功能稱為designated initializers
struct S a = {.y = 1}; // 反觀ANSI C只能從第一個欄位初始化
int a[100] = {[99] = 1}; // 把最後一個元素設定為1,其他都是0
int a[100] = {[99] = n}; // 把最後一個元素設定成n
int a[100] = {[n] = 5}; // 不合法
int a[n] = {[0] = 1}; // 不合法 vla 不能初始化
在ANSI C年代有個trick,如果要紀錄一筆資料,其中包含id和姓名,可以這樣設計
struct record {
int id;
char name[20];
};
但是實際上不會每個人的姓名都用到20個字,會浪費空間,所以就改用
struct record {
int id;
char *name;
};
不過這樣使用的時候需要多一次malloc,速度較慢且會造成記憶體破碎。
一種解決方法是這樣
struct record {
int id;
char name[1]; //有些編譯器寫0也可以 假設沒有padding
};
然後每次都malloc( sizeof(struct record) + strlen(name) );
就可以使整個struct都是在連續的記憶體上,只是這種方法並不被標準所保證。
在C99中則可以這樣寫
struct record {
int id;
char name[];
};
這功能被稱為flexible array members
--
※ 發信站: 批踢踢實業坊(ptt.cc)
◆ From: 140.119.162.50
推 VictorTom:學程式真是永無止盡啊, 就算只看C/C++兩套語言也是Orz 04/11 22:49
推 yauhh:那函數可以傳回compound literal嗎? 04/11 23:18
→ yauhh: 嗯...應該說可傳回"compund value"嗎? 04/11 23:19
推 LPH66:樓上的問題等於問說能不能回傳一個 struct 那答案就很明顯了 04/12 00:14
推 softwind:原來那個 char [0] 是這樣來的... 一直沒有搞懂過 04/12 00:14
→ yauhh:不盡相同,隨便return {1,'a'},回主程式能(struct S)嗎? 04/12 00:26
→ LPH66:唔? 不是有 struct S A = func(); 這種寫法嗎? 04/12 05:26
推 ledia:我猜測不能是因為會認不得是一個 type value 04/12 09:26
→ FRAXIS:所以加上強迫轉型就可以了吧.. 04/12 10:28
推 yoco315:什麼時候才有要語法層支援的 tuple XD 04/12 21:14