1.1 c是只能使用標量的語言(標量是指char、int、double和列舉型等資料型別,以及指標。相對地,陣列、結構體和共用體這樣的多個標量組合的型別,我們稱之為聚合型別)
#include int main(void)
如今c(ansi c)已經能夠讓我們整合的使用聚合型別了:
①結構體的一次賦值
②將結構體作為函式返回值傳遞
③將結構體作為函式返回值返回
④auto變數的初始化
結構體可以被整合利用,在實際程式設計中,應加以利用。如今還經常使用memcry()來進行結構體一次性賦值,其實可以以直接使用結構體一次賦值功能。
而如今的c還不能做到對陣列的整合利用。將陣列賦值給另外乙個陣列,或將陣列作為引數傳遞給其他函式等手段,在c中是不存在的。
#include int main(void)
; int b[3];
//無法實現陣列的一次性賦值
b[0] = a[0];
b[1] = a[1];
b[2] = a[2];
//b=a;
printf("%d\n%d\n",a,b);
return 0;
}
1.2 關於指標
「指標型別」由其他型別派生而成。因為有了指標型別,所以有了指標型別的變數與指標型別的值。幾乎在所有的處理程式中,指標型別的值實際是指記憶體的位址。
可以指向任何型別的的指標型別:void *
#include int main(void)
對指標加n,指標前進「當前指標指向的資料型別的長度 x n」。(嚴格地講,對於指標加減運算只允許指標指向陣列內地元素,或者超過陣列長度地下乙個元素。指標運算結果也只允許指標指向陣列內地元素,以及超過陣列長度地下乙個元素)
#include int main(void)
1.2.1空指標:
沒有指向任何乙個物件的指標(由系統保證空指標不指向任何實際的物件或者函式,未指向任何記憶體空間。沒有初始化的指標可能指向任意一處記憶體,所以指標一般都要在定義時初始化)。通常使用巨集定義null來表示空指標常量值,不過在c和c++中並不一樣。空指標確保它和任何非空指標進行比較都不會相等,因此經常作為函式發生異常時的返回值使用。
1 /* define null pointer value */
2 #ifndef null
3 #ifdef __cplusplus
4 #define null 0
5 #else /* __cplusplus */
6 #define null ((void *)0)
7 #endif /* __cplusplus */
8 #endif /* null */
在c中,null表示的是指向0的指標,而在c++中,null就直接跟0一樣了。
在大多數的環境中,空指標就是為0的位址。但是由於硬體狀況等原因,世上也許存在值不為0的空指標。有時在獲得乙個結構體之後,先使用memset()將它的記憶體區域清零再使用,(c語言提供了動態記憶體分配函式malloc()和calloc(),但是抱著「清零後比較好」的觀點,偏愛calloc()的人很多,這樣也許可避免一些難以再現的bug)。使用memset()和calloc()將記憶體區域清零,其實就是單純的使用0來填充位。當結構體成員中包含指標的時候,這個指標是否可以作為空指標來使用,最終由執行環境來決定。
null、0、'\0':
在c語言中,「當常量0處於應該作為指標使用的上下文中時,它就作為空指標使用」。
通常在c的字串中使用'\0'結尾。在c語言標準中,空字元定義為「所有的位為0的位元組稱為空字元」,控字元是值為0的字元。空字元通常使用'\0'表示。因為'\0'為常量,所以它等同於0。
綜上所述,當我們要置乙個指標為空時,應該用null,當我們要給乙個字串新增結束標誌時,應該用'\0'。
1.2.2 swap函式
/*常用的幾種swap函式*/
#define swap1(a,b) (a += b, b = a - b, a -= b)
#define swap2(a,b)
#define swap3(a, b)
void swap(int *a, int *b)
1.3陣列和指標的微妙關係正如之前1.2中所講:對指標加n,指標前進「當前指標指向的資料型別的長度 x n」。因此,給指向陣列的某個元素的指標加n後,指標會指向n個之後的
#include int main(void)
; int array2[5]=;
int *p;
int i;
for(p = &array1[0];p != &array1[5];p++)
p = &array2[0];
for(i = 0;i < 5;i++)
return 0;
}
無論寫成p++,還是*(p+i),都不容易閱讀。試試最初的例子中的array[i]這樣的方式更容易理解。
1.3.1下標運算和陣列是沒有關係的
在上面**中p = &array1[0]也可以寫成下面這樣:
p = array1;
另外,程式中 *(p+i) 也可以寫成 p[i]。*(p+i) 和 p[i] 是同樣的意思,可以認為後者是前者的簡便寫法。
要點:p[i]是*(p+i)的簡單寫法,下標運算子[ ]原本只有這種用法,它和陣列無關。認為[ ] 和陣列沒有關係,這裡的[ ]是指在表示式**現的下標運算子[ ]。宣告中的[ ],還是表達陣列的意思。宣告中[ ]和表示式中的[ ]意義完全不同。
1.3.2試圖將陣列作為函式的引數進行傳遞
先看下面的一段**:
#include #include #include int get_word(char *buf, int buf_size, file *fp)
} while ((ch = getc(fp)) != eof && isalnum(ch));
buf[len] = '\0';
return len;}
int main(void)
return 0;
}
main()中宣告的陣列 buf,在 get_word()中被填充值。在 main()中,buf 作為函式的引數傳遞,因為這裡是在表示式中,所以 buf 可以解讀成「指向陣列初始元素的指標」。因此,接受 buf 的 get_word() 才可以像:
int get_word(char *buf, int buf_size, file fp)
這樣,合法地接受 char *。
一旦在 get_word 中使用下標運算子訪問 buf 的內容,倒還真的會讓人感覺從 main()傳遞過來的是 buf 這樣的陣列。顯然這是個錯覺,無論如何,從 main()傳遞過來的是指向 buf 的初始元素的指標。準確地說,在 c 中是不能將陣列作為函式引數進行傳遞的。但是,可以通過傳遞指向初始元素的指標來達到將陣列作為引數進行傳遞的目的。
1.3.3 宣告函式形參的方法
只有在宣告函式形參時,陣列的宣告才可以被解讀成指標(陣列形參退化為指標)。
比如,對於
int func(int a)
編譯器可以針對性地解讀成:
int func(int *a)
即使定義了元素個數,編譯器也是無視的。
必須要引起注意的是,int a和 int *a 具有相同意義,在 c 的語法中只有這麼一種情況。
要點:在下面宣告的形參,都具有相同的意義。
int func(int *a); /寫法1*/
int func(int a); /*寫法2*/
int func(int a[10]); /*寫法3*/
寫法 2 和寫法 3 是寫法 1 的語法糖。
ps:程式設計過程中往往存在如下陷阱:將陣列作為函式引數傳入,試圖在函式中求取陣列的長度。這樣做是不行的。在將陣列作為函式引數傳入的同時,往往也會將陣列的長度傳入。
如下**:
#include int array[5]=;
int * p = array;
int func(int array)
int main(void)
輸出結果:
sizeof(array):8
sizeof(array[0]):4
num1 is :2
num2 is :5
sizeof(array): 20 sizeof(array[0]): 4
sizeof(p): 8
征服C指標
更多關於 征服c指標 內容簡介 計算機書籍 征服c指標 被稱為日本最有營養的c 參考書。作者是日本著名的 毒舌程式設計師 其言辭犀利,觀點鮮明,往往能讓讀者迅速領悟要領。征服c指標 中結合了作者多年的程式設計經驗和感悟,從c 語言指標的概念講起,通過實驗一步一步地為我們解釋了指標和陣列 記憶體 資料...
試讀《征服C指標》
說起學習c語言,我想大家都閱讀過清華大學出版社出版的譚浩強版的 c程式設計 這本書是我的c語言啟蒙。我的程式設計道路也是從學習c語言開始的。剛接觸c語言時感覺理解起來非常費力。怎麼也想不通i 和 i等一系列的不是問題的問題,因為每次問老師時,老師也說不清楚,最後得到的答案卻是,就是這樣規定的你就這樣...
《征服C指標》試讀感想
其實起初看到這本書不是在這裡,而是在以為同學那裡,當時由於時間限制,而且看了看內容,是一種 b 近乎於吐槽的語言方 b 式,覺得可能有點兒虛,拿起來翻了翻就扔給他了。但是這個名字 征服c指標 起的,實在是讓我有看一看的衝動,所以就上網搜了一下 企圖能夠有個電子版啥的 oops 結果卻發現了一些這本書...