1.define宏定義
define中的#和##:
(1)#可以將define中的參數轉化為字符串,例如:
#define PRINT(x) printf(#x “ is %d”, x);
(2)##可以將define中的參數轉化為某個標識符的一部分,例如:
int tmp_a = 23;
#define PRINT(x) printf(#x “ is %d”, tmp_##x);
define的位置:
宏定義可以出現在所有函數外部或者某函數內部,遵循兩個規則:
第一, 內部定義覆蓋外部定義。如果全局的宏定義與某內部宏定義重名時,VC6.0編譯器會提示warning但不出錯,且以內部宏定義為準。
第二, 定義點后均可使用,不以函數內外劃分作用域,僅以文本中出現位置前后劃分。
define解析順序:
#define f(a,b) a##b
#define g(a) #a
#define h(a) g(a)
void macrotest(void)
{
printf("%s,",h(f(1,2)));
printf("%s\n",g(f(1,2)));
}
最后答案是:12, f(1,2)
分析:解析一個串從左至右,一遍之后再從頭開始。所以h(f(1,2)) -> g(f(1,2)) -> g(12) -> 12,而g(f(1,2)) -> #(f(1,2)) -> f(1,2)。
define的缺陷:
第一, 對于所有的function-like macros,所有的參數都要括起來,以防止macros(a+b)的情況出現;而且要注意是否有類似macros(i++)的情況出現,防止在文本替換后i++執行多次。
第二, 宏不做類型檢查,而且預處理展開后消失于無形,編譯出錯了很難找到錯誤。
第三, 使用宏后該“函數”不能取址,不能作為函數指針傳遞給另一個函數。
第四, 一般使用inline函數代替宏函數,類似的,一般使用const變量代替宏變量。
2.strncpy和strncat
strncpy(dest, src, size);
使用strncpy(a, b, size)函數時,根據size值分兩種情況:
第一,size比字符串b長度大時,將b字符串賦給a,再將a中size-b.length的多余位置賦值為\0。
第二,size比字符串b長度小時,將size長度的b子字符串賦給a,但不自動在后面添加\0。
strncat(dest, src, size);
使用strncat(a, b, size)函數時,根據size值分兩種情況:
第一,size比字符串b長度大時,將b字符串賦給a第一個\0結束符處,并自動在右面添加\0,多余的size-b.length位置不賦值。
第二,size比字符串b長度小時,將size長度的字符串賦給a第一個\0結束符處,并且自動在后面添加\0。
3.printf和scanf
printf(“%x, %x, %x”, a, b, c);
printf注意事項:
第一, pirntf參數從右向左依次壓棧。
第二, 字符串%×和后面參數從左至右依次映射,當參數多出時可忽略不計。
第三, 字符串%×和后面參數從左至右依次映射,當%×多出時,打印出來的結果不可預測(因為VC下參數從右向左壓棧,所以多出的%×只能對應不可預知的內存)。
其他
4.rand()函數的最大取值是0X7fff,也就是2的15次方-1。
5.對于數組char a[100],sizeof(a)的值是100,表示數組大小,而sizeof(&a)按道理來說應該是4,表示指向數組a的指針的大小,但是某些MSVC版本對arrayName和&arrayName是不區分的,需要安裝sp1的MSVC的補丁才可去掉這個bug。
6.C語言中,不寫返回類型的函數,一般默認為int型,而C++中必須指明返回類型。但在一般的編譯器實現中,可能會做放寬處理,例如VC6.0中可以接受C++函數沒有返回類型,默認為int。
7.返回值為數組指針
如果函數返回值是int(*)[NUM]類型,不可寫成int(*)[NUM] func() {}的形式,而應該寫成:
int (*func)() [NUM] {}
或者使用typedef來簡化:
typedef int(*)[NUM] type;
type func() {}
那么,如何保存函數的返回值呢,具體如下:
int a[num1][NUM];
int (*b)[NUM] = &a[num2];
b = func();
8.結構體
結構體有賦值操作,但是沒有比較操作,可以重載==等這些比較運算符;同時,最好別用memcmp函數進行結構體的比較操作,因為要考慮到結構體的對齊問題,且填充的字節是隨機的。
9.函數指針
void func() {}
printf(“%p %p %p”, func, &func, *func);
其結果是一樣的,原因在于:
函數名就是函數名,除了少量情況,它會退化為函數指針,即發生function-to-pointer轉換。fun單獨放著的時候就會發生退化,而在&fun的情況下不會退化,所以單獨的fun和&fun的類型、值都一樣。而*fun則是fun先發生退化,變成函數指針,*變成函數類型,然后再退化成函數指針,所以函數類型怎么*都一樣。(maybe)
10.(int&)a和(int)a的區別
(int&)a不經過轉換,直接得到a在內存單元的值;(int)a則是a在內存中的值轉換成int類型,那么存在兩種情況:
第一, a類型是int,此時(int&)a和(int)a是相等的。
第二, a類型是float等,由于float在內存中存儲的形式是符號位+指數+尾數,而階碼采用增碼,為數采用源碼,與int的存儲形式不同。(int)a會先將內存中的值轉換成int類型,然后給a,而(int&)a則直接將內存中的值給a,不經過轉換,所以此時兩者不相等。