一些雜談,提供個人Coding 的經驗
1. 無窮迴圈的例子: 沒有注意宣告的變數範圍上限或邏輯錯誤而溢位產生無窮迴圈
- for (unsigned char x=0; x<=0xff; x++) ;
- for (unsigned char x=0xff; x>=0; x--) ;
用指標來管理記憶體,而雙指標(指標的指標)就是用指標管理指標
實務上在函式間傳遞指標的位址時, 就必需要傳遞指標的指標。舉個記憶體管理的例子,假如我有一串link list如下p1~p6,就可以透過雙指標pp start/pp tail指定鏈結串鏈其中一段的起始節點位址(&p2)和尾部節點位址的(&p5), 而PP start/ PP tail就是雙指標的型態
3. Big Endian Vs Little Endian
假如我有一串binary,他是一連串的DWORD組成,試問該如何去解釋這個binary內容呢。答案是在不同機器上就有不同的解讀方法。
+ #define VAL_TO_ARRAY_LITTLE_ENDIAN( array, val ) \
+ do { \
+ (array)[1] = (val) >> 8; \
+ (array)[0] = (val) & 0xFF; \
+ } while(0)
b. Big Endian : 高位元放低位址 (有些spec欄位會定義成這種)
4. 宣告64位元變數
比較新的GCC版本都有支援宣告64位元變數,一般在Linux kernel code看到常數後面跟著ull,就代表他的定義是64位元變數的常數。我自己使用上不加ull也不會產生問題(Watcom/gcc),但是在輸出螢幕時必須使用%llX,(只有%X顯示不出bit 63:32),以下提供各長度型態宣告方法
+ typedef unsigned long long QWORD, u64;
+ typedef unsigned int DWORD, u32;
+ typedef unsigned short WORD, u16;
+ typedef unsigned char BYTE, u8;
5. 巨集"敘述"化
巨集 A用 do{} while(0) 或 {} 將程式寫成一個敘述,在呼叫時不會產生錯誤。同樣的程式寫成巨集 B,這樣呼叫方式則會產生錯誤
+ // 巨集A
+ #define TMACRO(pstr) {//do{\
+ printf("log:\n");\
+ printf(pstr);\
+ }//while(0)
+ // 巨集B
+ #define TMACRO(pstr)\
+ printf("log:\n");\
+ printf(pstr)
+ // Call TMACRO
+ if (x>=y)
+ TMACRO("x>=y\n");
+ else
+ TMACRO("x<y\n");
6. 前置處理器
廣泛使用在韌體版本控制(不同版本通常硬體設定都會不同)
+ //#define XX
+ #if !defined(XX)
+ printf("not define XX\n");
+ #elif defined(XX) && (ZZ > 1)
+ printf("define XX and ZZ > 1 \n");
+ #else
+ printf("define XX and ZZ == 0\n");
+ #endif
+
+ #ifdef XX
+ printf("define XX\n");
+ #endif
+
+ #ifndef XX
+ printf("not define XX\n");
+ #endif
7. 在程式中顯示build code time
提供時間做參考,以免同一版程式被更改了但是卻沒進版
+ printf( "%s %s", __DATE__, __TIME__); //The __DATE__ and __TIME__ macros are predefined by the compiler
加額外的information, 如哪一個檔案/函式/第幾行出錯
+ printf( "File : %s\n", __FILE__ );
+ printf( "Function : %s\n", __FUNCTION__ );
+ printf( "Line : %d\n", __LINE__ );
8. 盡量不要用goto, exit()這類的語法,因為這類的語法屬於直接跳躍指令造成程式可讀性變差
9. strlen 和 sizeof 的誤用
strlen()回傳的是字串長度(所以不含結束字元'\0')而sizeof()是回傳此變數型態宣告的大小
+ char p[10]="123";
+ printf(" %d\n", sizeof(p)); // print 10
+ printf(" %d\n", strlen(p)); // print 3
7. 善用可變數目參數,在這例子是把printf, fprintf用define統合寫在一起處理
+ #define HybridPrintf(fp, ...) {\
+ printf( ##__VA_ARGS__);\
+ fprintf(fp, ##__VA_ARGS__);\
+ }
8. array的初始化
+ char buffer[] = { 'a', 'b', 'c' };
可把初始化那個 index 寫成如下:
+ char buffer[] = { [0] = 'a', [1] = 'b', [2] = 'c' };
9. 指標的宣告
下行是錯誤的
+ int* p1, p2; // p2 的 type 是 int
要改成
+ int *p1, *p2; // 但是不好
或改成
+ typedef int * pInt;
(待續)
+ #define TMACRO(pstr) {//do{\
+ printf("log:\n");\
+ printf(pstr);\
+ }//while(0)
+ // 巨集B
+ #define TMACRO(pstr)\
+ printf("log:\n");\
+ printf(pstr)
+ // Call TMACRO
+ if (x>=y)
+ TMACRO("x>=y\n");
+ else
+ TMACRO("x<y\n");
6. 前置處理器
廣泛使用在韌體版本控制(不同版本通常硬體設定都會不同)
+ //#define XX
+ #if !defined(XX)
+ printf("not define XX\n");
+ #elif defined(XX) && (ZZ > 1)
+ printf("define XX and ZZ > 1 \n");
+ #else
+ printf("define XX and ZZ == 0\n");
+ #endif
+
+ #ifdef XX
+ printf("define XX\n");
+ #endif
+
+ #ifndef XX
+ printf("not define XX\n");
+ #endif
7. 在程式中顯示build code time
提供時間做參考,以免同一版程式被更改了但是卻沒進版
+ printf( "%s %s", __DATE__, __TIME__); //The __DATE__ and __TIME__ macros are predefined by the compiler
加額外的information, 如哪一個檔案/函式/第幾行出錯
+ printf( "File : %s\n", __FILE__ );
+ printf( "Function : %s\n", __FUNCTION__ );
+ printf( "Line : %d\n", __LINE__ );
8. 盡量不要用goto, exit()這類的語法,因為這類的語法屬於直接跳躍指令造成程式可讀性變差
9. strlen 和 sizeof 的誤用
strlen()回傳的是字串長度(所以不含結束字元'\0')而sizeof()是回傳此變數型態宣告的大小
+ char p[10]="123";
+ printf(" %d\n", sizeof(p)); // print 10
+ printf(" %d\n", strlen(p)); // print 3
7. 善用可變數目參數,在這例子是把printf, fprintf用define統合寫在一起處理
+ #define HybridPrintf(fp, ...) {\
+ printf( ##__VA_ARGS__);\
+ fprintf(fp, ##__VA_ARGS__);\
+ }
8. array的初始化
+ char buffer[] = { 'a', 'b', 'c' };
可把初始化那個 index 寫成如下:
+ char buffer[] = { [0] = 'a', [1] = 'b', [2] = 'c' };
9. 指標的宣告
下行是錯誤的
+ int* p1, p2; // p2 的 type 是 int
要改成
+ int *p1, *p2; // 但是不好
或改成
+ typedef int * pInt;
+ pInt p1, p2;
(待續)
待續文章哪裡連呢?
回覆刪除