指標——C語言的靈魂

出版圈郭志敏發表於2013-03-25

為什麼說C指標是C語言的靈魂?

來自讀者對C和指標的解說

  1. 他可以直接訪問硬體,這是靈活性和效率的體現,程式離硬體越近自然效率越高,當然運用不當也可導致效率低下

  2. 難掌握及太危險,如果對指標理解含混,訪問過程不當易導致程式奔潰或隱藏潛在危險

  3. 指標作用總的說是調高程式執行效率,原因是它對c語言中定義的各種資料結構進行地址傳遞,而不需要進行不斷地進行值傳遞。理解起來可以聯想一下資料共享與建立副本的區別。

注:歡迎各位程式猿們繼續補充

挖掘C語言的靈魂,先看《征服C指標》這本書

enter image description here

讀者對本書的評價:

  • 針對性極強,全書以一種精準但又不考究的方式解讀C語言指標。看過經典的“C和指標”後再看看日本程式設計師對指標的理解,也許會獲得又一番的感悟。這是最棒的C書 、
  • 日本人都是工匠,從精密機械到各種探測器,從av情色片到美食,小日本幹任何事都精益求精。 我看過小日本的幾本計算機書,binary hack, debug hack, 以及這本,全都是精品。 他們的書邏輯性不強,特點是人的因素多,注重細節,教給讀者訣竅,讀來暢快淋漓,愛不釋手。 可能我的性格里也有黑客的成份吧,不喜歡那種冷冰冰的知識羅列的書

買前必讀:

這是一本關於C 語言的陣列和指標的書。

■ 本書的讀者群雖然定位於“學習過C 語言,但是在指標的運用上遇到困難”的讀者,但還是能隨處可見一些高難度的內容。那是因為我也不能免俗,偶爾也喜歡把自己掌握的知識拿出來顯擺一下。

■ 對於初學者,你完全沒有必要從頭開始閱讀。遇到還不太明白的地方,也不要過分糾結。閱讀中可以跳躍章節。對於第0章和第1章,最好還是按順序閱讀。如果認為第2章有點難度,你可以先去啃第3章。如果第3章也不懂,不妨嘗試先去閱讀第4章。這種閱讀方式是本書最大的賣點。

■ 在本書中,我會經常指出一些“C的問題點”和“C的不足”。可能會有一些讀者認為我比較討厭C語言。恰恰相反,我認為C是一門偉大的開發語言。倒不是因為有“情人眼裡出西施”、“能幹的壞小子也可愛”這樣的理由,畢竟在開發現場那些常年被使用的語言中,C語言還是有相當實力的。就算是長得不太帥,但論才幹,那也是“開發現場的老油條”了。

迷你書下載

精彩片段:

應該記住:陣列和指標是不同的事物

為什麼會引起混亂?

首先,請允許我強調一下本章的重要觀點。

C 語言的陣列和指標是完全不同的。

大家都說C語言的指標比較難,可是真正地讓初學者“撓牆”的,並不是指標自身的使用,而是“混淆了陣列和指標”。此外,很多“坑爹”的入門書對指標和陣列的講解也是極其混亂。

比如,K&R 中就有下面一段文字(p.119), C 語言的指標和陣列之間有很強的關聯關係,因此必須將指標和陣列放在一起討論。

很多C程式設計師認為“陣列和指標是幾乎相同的事物”,這種認識是引起C的混亂的主要原因。

從圖3-17中可以一目瞭然地看出,陣列是一些物件排列後形成的,指標則表示指向某處。它們是完全不同的。

enter image description here

帶著“陣列和指標是幾乎相同的事物”這樣的誤解,初學者經常寫出下面這樣的程式碼:

int *p;

p[3] = ……     ←突然使用沒有指向記憶體區域的指標
——自動變數的指標在初期狀態,值是不定的。
char str[10];
    .
    .
    .
str = “abc”;    ←突然向陣列賦值

——陣列既不是標量,也不是結構體,不能臨時使用。

int p[]; ←使用空的[]宣告本地變數 ——只有在“函式的形參的宣告”中,陣列的宣告才可以被解讀成指標。 對於陣列和指標,它們在哪些地方是相似的,又在哪些地方是不同的?不好意思,可能在下面會出現和前面重複的內容。

表示式之中 在表示式中,陣列可以被解讀成指向其初始元素的指標。所以,可以寫成下面這樣:

int array[10];

p = array;         ←將指向array[0]的指標賦予p

可是,反過來寫成下面這樣,

array = p;

就是不可以的。確實,在表示式中array 可以被解讀成指標,可是,本質上它其實是被解釋成了&array[0],此時的指標是一個右值*。(此時的指標是右值這個理由之外,在標準中,陣列也不是“可變更的左值”。)

比如,對於int型別的變數a,a = 10;這樣的賦值是可以的,但肯定沒有人想做a + 1 = 10;這樣的賦值吧。儘管a 和a + 1 都是int,但是a + 1沒有對應的記憶體區域,只是一個右值,所以不能被賦值。同樣的道理,array也不能被賦值。此外,對於下面這個指標,

int *p;

如果p指向了某個陣列,自然可以通過p[i]的方式進行訪問,但這並不代表p就是指標。

p[i]只不過是*(p + i)的語法糖,只要p正確地指向一個陣列,就可以通過p[i]對陣列的內容進行訪問,如圖3-18所示。

enter image description here

如果是“指標的陣列”和“陣列的陣列”,就會有很大的不同。

char *color_name[] = {        ←指標的陣列
    “red”,
    “green”,
    “blue”,
};

對以上的程式碼進行圖解(參照圖3-19),

![enter image description here][5]

char color_name[][6] = {
    “red”,
    “green”,
    “blue”,
}

對以上的程式碼進行圖解(參照圖3-20),

enter image description here

以上兩種情況都可以用color_name[i][j]的方式對陣列進行訪問,但是記憶體中資料的佈局是完全不同的。

宣告 只有在宣告函式的形參的時候,陣列的宣告才能解讀成指標的宣告(參照3.5.1節)。

以上的語法糖,與其說使C變得更加容易理解,倒不如說它使C語言的語法變得更加混亂。是不是有很多人這麼想?我就是其中的一個(雖然使用這個語法糖可以讓多維陣列作為引數被傳遞時更容易理解……)。而且K&R的說明更是使這種混亂局面雪上加霜。

在不是宣告函式的形參的時候,陣列宣告和指標的宣告是不可能相等的。

使用extern 的時候是最容易出現問題的(參照3.5.2節)。另外,宣告區域性變數或者結構體的成員時,寫成

int hoge[];

會引起語法錯誤(對於結構體的成員,在ISO C99 中是允許這種寫法的)。

存在陣列初始化表示式的情況下,可以使用空的[],但這是因為編譯器能夠計算出陣列元素的個數,所以可以省略書寫元素個數。僅此而已,這種特徵和陣列扯不上任何關係。

要 點 【非常重要!!】 •陣列和指標是不同的事物。

惡名昭著的指標究竟是什麼

關於“指標”一詞,在K&R 中有下面這樣的說明(第5 章“指標和陣列”的開頭部分): 指標是一種儲存變數地址的變數,在C 中頻繁地使用。

其實在表達上,這樣的說明是有很大問題的。總會讓人感覺,一旦提起指標,就要把它當作變數的意思。實際上並非總是如此。

此外,在C 語言標準中最初出現“指標”一詞的部分,有這樣一段話:

指標型別(pointer type)可由函式型別、物件型別或不完全的型別派生,派生指標型別的型別稱為引用型別。指標型別描述一個物件,該類物件的值提供對該引用型別實體的引用。由引用型別T 派生的指標型別有時稱為“(指向)T 的指標”。從引用型別構造指標型別的過程稱為“指標型別的派生”。這些構造派生型別的方法可以遞迴地應用。

這段話的內容也許會讓你一頭霧水(既然是標準,那總要有點標準的範兒吧)。那就讓我們先關注第一句話吧,那裡出現了“指標型別”一詞。

提到“型別”,立刻會讓人想起“int 型別”、“double 型別”等。同樣,在C 語言中也存在“指標型別”這樣的型別。

“指標型別”其實不是單獨存在的,它是由其他型別派生而成的。以上對標準內容的引用中也提到“由引用型別T 派生的指標型別有時稱為‘(指向)T 的指標’”。

也就是說,實際上存在的型別是“指向int 的指標型別”、“指向double的指標型別”。 因為“指標型別”是型別,所以它和int型別、double型別一樣,也存在“指標型別變數”和“指標型別的值”。糟糕的是,“指標型別”、“指標型別變數”和“指標型別的值”經常被簡單地統稱為“指標”,所以非常容易造成歧義,這一點需要提高警惕*。

(*至少本書還是盡力將這些說法進行區別的,但有時候,無論怎麼寫也做不到自然地表述想要表達的意思,最後只好投降……非常抱歉。)

要 點

先有“指標型別”。 有了“指標型別”,才有了“指標型別的變數”和“指標型別的值”。

比如,在C中,使用int 型別表示整數。因為int是“型別”,所以存在用於儲存int型的變數,當然也存在int型的值。指標型別同樣如此,既存在指標型別的變數,也存在指標型別的值。

因此,幾乎所有的處理程式中,所謂的“指標型別的值”,實際是指記憶體的地址。

變數的內容是儲存在記憶體的某個地方的,“某個地方”的說法總是會讓人產生困惑,因此,就像使用“門牌號”確定“住址”一樣,在記憶體中,我們也給變數分配“門牌號”。在C的記憶體世界裡,“門牌號”被稱為“地址”。

相關閱讀:

相關文章