開始讀《C專家編程》之前,,有一個(gè)很擔(dān)心的問題:94年出的講語言的書,,在現(xiàn)在(2012)還有多少是適用的。因此,,一邊讀,,一邊用VS2010做實(shí)驗(yàn),。最后發(fā)現(xiàn)大部分內(nèi)容都還在用,。讀完后,覺得最精彩的部分有二:一是講解如何理解聲明,,二是深入地講解數(shù)組名與指針,。下文是將看書過程中所做的筆記進(jìn)行的整理。 p.s: 以下代碼均在VS2010測試過
1. 使用無符號數(shù)時(shí)要特別注意(不推薦使用無符號數(shù))
當(dāng)無符號數(shù)與有符號數(shù)在同一條表達(dá)式中出現(xiàn)時(shí),,有符號數(shù)會被轉(zhuǎn)換為無符號數(shù),。e.g:
int feng = -1;
unsigned int yan = 5;
bool result = (feng < yan) ? true : false; //最后的結(jié)果會是false
原因是C語言在計(jì)算含有不同類型的表達(dá)式時(shí),會將類型向上提升,。在本例中,,int被提升了unsigned int,從而使-1的補(bǔ)碼被解析為很大的整數(shù)
2. 相鄰的字符串常量會被自動合并成一個(gè)字符串
e.g:
char *str[] = {"feng" "yan", "zero"}; //"feng"和"yan"被合并成一個(gè)了:"fengyan"
3. 易出錯(cuò)的優(yōu)先級
.高于* e.g: *p.f 正確的理解:*(p.f)
[]高于* e.g: int *ap[] 正確的理解:int *(ap[]) ap是個(gè)數(shù)組,,其元素為int*
函數(shù)()高于* e.g: int *fp() 正確的理解:int* fp() fp是返回int*的函數(shù)
==和!=高于位操作符 e.g: val & mask != 0 正確的理解:val & (mask != 0)
==和!=高于賦值符 e.g: c = getchar() != EOF 正確的理解:c = (getchar() != EOF)
算術(shù)運(yùn)算高于移位運(yùn)算 e.g: msb<<4 + lsb 正確的理解:msb << (4 + lsb)
逗號運(yùn)算符優(yōu)先級最低 e.g: i = 1, 2 正確的理解:(i = 1), 2
4. 理解聲明,,定義,typedef語句的步驟
a. 找標(biāo)識符
b. 找被括號括起來的部分
c. 找后綴操作符,,如果是(),則表示是函數(shù),;如果是[],,則表示是數(shù)組
d. 找前綴操作符,如果是*,,則表示“指向XX的指針”
e. 找const和volatile,,如果const,volatile后面緊跟類型(如int,,long),,那么它作用于類型,其它情況下,,作用于它左邊緊鄰的項(xiàng)
e.g:
int const * zero; //zero是一個(gè)指針,,指向一個(gè)常量整形
char (*zero)[20]; //zero是一個(gè)指針,,指向一個(gè)有20個(gè)char元素的數(shù)組
typedef void (*ptr_to_func)(int); //ptr_to_func是新類型名,這種類型是一個(gè)函數(shù)指針,,指向接收一個(gè)int參數(shù),,并返回void的函數(shù)
char* const * (*zero)(); //zero是一個(gè)函數(shù)指針,該函數(shù)無參數(shù),,并返回一個(gè)指針,,返回的指針指向一個(gè)常量指針
char* (*zero[10])(int **p); //zero是一個(gè)數(shù)組,元素是函數(shù)指針,,其指向的函數(shù)授受一個(gè)二維指針,,并返回一個(gè)指向char的指針
void (*signal(int sig, void (*func)(int)))(int); //signal是一個(gè)函數(shù),該函數(shù)接收一個(gè)int,,一個(gè)函數(shù)指針,,并返回一個(gè)函數(shù)指針
5. 左值與右值
左值通常表示存儲結(jié)果的地方(地址),其值在編譯時(shí)可知
右值通常表示地址的內(nèi)容,,其值通常要到運(yùn)行時(shí)才知道
6. 指針與數(shù)組名不等同的情況(定義為數(shù)組,,卻聲明為指針,或者反過來)
前提知識(假設(shè)有定義:int array[10], *ptr;):
a. 使用數(shù)組名下標(biāo)訪問(如:array[1]),,會直接將數(shù)組名的地址加上偏移值作為變量的地址(即array[1]的地址)
b. 使用指針下標(biāo)訪問(如:ptr[1]),,會先取指針指向的內(nèi)容,然后將這個(gè)內(nèi)容加上偏移值作為變量的地址(即ptr[1]的地址)
不等同的原因:
當(dāng)定義為數(shù)組,,卻聲明為指針時(shí),,相當(dāng)于用指針下標(biāo)訪問的方法來解析一個(gè)數(shù)組名下標(biāo),即先取數(shù)組第0號元素的內(nèi)容,,然后將這個(gè)內(nèi)容加上偏移值作為變量的地址,,從而訪問了不該訪問的東西。反之亦然,。
7. 指針與數(shù)組等同的情況
a. 編譯器將表達(dá)式中的數(shù)組名當(dāng)作指向該數(shù)組第0號元素的指針,,下標(biāo)當(dāng)作指針的偏移量,即array[i]會被當(dāng)作*(array + i)
b. 編譯器將函數(shù)參數(shù)聲明中的數(shù)組名當(dāng)作指向該數(shù)組第0號元素的指針,,即在函數(shù)內(nèi)部得到的是指針,,而不是數(shù)組名 基于a情況,可知這條謠言是假的(至少在一維數(shù)組中一定不成立):
用指針迭代數(shù)組比用下標(biāo)迭代數(shù)組更快
基于b情況,,可解釋為什么在傳遞數(shù)組后,,不能用以下方法計(jì)算數(shù)組長度
int ArrayLength(int arr[]) {
return sizeof(arr) / sizeof(arr[0]); //返回值必定是1,因?yàn)榇藭r(shí)的arr是一個(gè)指針,,而不是數(shù)組名
}
注意b情況的將數(shù)組改寫為指針并不是遞歸定義的,,e.g:
實(shí)參 char zero[10][10] 被改寫為 char (*zero)[10],這里將數(shù)組的數(shù)組改寫為數(shù)組的指針
實(shí)參 char *zero[10] 被改寫為 char **zero,,這里將指針數(shù)組改寫為指針的指針
實(shí)參 cahr (*zero)[10] 不改變,,因?yàn)榇藭r(shí)的zero是指針,,而不是數(shù)組
8. interposition
interposition指用戶定義的函數(shù)取代庫中聲明完全相同的函數(shù),注意這不是指重載,,而是指下面這種:
void zero(); //user defined function
void zero(); //library function
出現(xiàn)interposition時(shí),,要特別注意以下情況:
void zero(); //user defined function
int main() {
zero(); //調(diào)用用戶定義的函數(shù)zero,而不是庫函數(shù)zero
FengYan(); //假設(shè)這是另一個(gè)庫函數(shù),,并且函數(shù)內(nèi)調(diào)用庫函數(shù)zero,,此時(shí)由于interposition,變成調(diào)用用戶定義的zero
return 0;
}
備注:
出現(xiàn)interposition時(shí),,在VS2010會出現(xiàn)warning: inconsistent dll linkage
9. 堆棧段作用
a. 存儲局部變量
b. 函數(shù)調(diào)用時(shí),,存儲有關(guān)的維護(hù)信息
c. 用作暫時(shí)存儲區(qū)。e.g: 計(jì)算一個(gè)很長的表達(dá)式時(shí),,會把部分結(jié)果先壓到堆棧中 |
|