標籤:

指針的指針定義為什麼用int ** ptr,而不是int *ptr?

假如int a=10; int *b=a;指針b本身就是一個變數,b指針變數的內容為變數a的地址,現在要把指針b的地址給指針變數c ,c=b;按照功能來說c指針變數本身和b指針變數一樣,都是指向一個地址,只不過c指向的是一個存儲指針的地址,而b是一個指向一個變數的地址,但從物理內存上來講都是存儲一個指向地址的指針變數而已,為什麼定義的時候用 int **c=b;而不用int *c=b?還是使用int ** c僅僅表明它是個指向指針的指針用來與**c取值運算進行格式對齊?

當使用int *c=b時,只能強制轉化才能正確運行輸出,如下:

int a=10; int *b=a; int *c=b;

printf("%d
",*(int *)(*c));


建議不要用無類型的彙編或 B 的思路理解指針。

請明確「類型」這一概念。 c 指向的是 int* 類型對象,而 b 指向的是 int 類型對象;既然指向的對象類型不同,它們就是不同類型的指針,自然要用不同的方式聲明/定義。


在機器語言和彙編時代,只有地址的概念,沒有類型的概念。開發人員只好將數據存儲在一定地址空間中,並通過地址來訪問這些數據。

但是,但是……,拿一個中型程序來說,裡面數據長度是千奇百怪的,有1位元組,2位元組,以及4位元組的,甚至放的是一個整數表(拿C這種高級語言來說,就是一個int型數組)。但是這些數據的長度,彙編器是無法知道的,只有開發人員自己才知道。所以在代碼中需要格外關注數據長度,並且在每次讀寫過程中,都需要嚴格遵守這種開發人員自定義的數據語義。如果不小心,很容易出錯。

誰告訴你,C語言的指針,就是一個地址?

如果你回答是「將C編譯器生成的二進位進行反編譯,看到每個指針,對應的彙編語義就是表示為一個地址」

那我只能說,你肉眼所見到的事實,並不一定是真相。真相需要大腦去思想,歸納之後,才能形成科學的認識。

C語言裡面的定義的指針,它除了表示一個地址,它還帶有類型信息。

這個類型信息,用來告訴你,在這個地址空間上,存放著什麼類型的變數。

打個比如,有如下的代碼片段:

int a;
int *p = a;

假設p的指針值為0x08004000,並且int類型長度為4位元組。

那麼p將告訴你,[0x08004000, 0x08004004) 內存空間上存放著一個int類型。

如果你只從0x08004000地址中,只讀取兩個位元組,那是錯誤的。

同樣道理,以下代碼片段:

int a;
int *b = a;
int **c = b;

是告訴你,c的指針值是另一個指針(b),而該指針則指向一個int變數。

如果你刻意要較真,認為指針就是一個地址,並且舉出下面的例子:

int a;
int *b = a;
int * c = b; // 請留意這裡

最後一句賦值,實際上是將類型信息丟棄了。因為在編譯器看來,C 就表示一個指針,它指向的是一個整數,只是你自己將它解釋成指針而已。

printf("%d
",*(int *)(*c)); // 開發人員,將*c解釋成指針,編譯器是不認帳的喔

為什麼將一個整數解釋成一個指針沒有問題呢?那個因為湊巧,你把它放到64位架構上試試看看。

因為:事實不一定是真相,有時只有一種巧合而已。不管是時間的巧合,還是空間的巧合,它將永遠是一個巧合,永遠不是真相。


因為指針也是有類型的,T 取地址的類型就是 T*,而且指針類型之間也不存在隱式轉換(void * 這種 Opaque Type 除外,它可以接受任何指針類型),這是為了類型安全考慮的。


int a;//這是你家

int* a_ptr = a;//這是你家門牌號

int** a_ptr2 = a_ptr;//這是個郵箱位置,去那裡你能拿到有你家門牌號的信。


你要知道指針的含義是什麼。

指針不是地址,指向對象的類型是它包含的信息中的一部分,用來解釋指向的對象。像你這樣搞,把指針當成純地址看這就是丟掉了類型信息。


C/C++中 int* 是一種複合類型,表示指向int的指針。我覺得可能是樓主對int *的理解出問題了。

一個指針聲明的分割的理解方式應該是(int*)( b)這樣間隔開來看的,其中int和*中在哪裡添加空格對於編譯器沒有區別。

你可以這樣int *b 還可以這樣int * b 甚至可以這樣 int* b

我覺得題主開始的理解方式是「*」和「b」是一對,但其實「int」和「*」才是一對呢。


編程語言執行過程不要通過臆測去了解,要了解語言發明者的意圖 。建議看看kR的」C程序設計語言「的複雜聲明那幾頁。

以下為轉載:

綜上:

我認為不存在 int* 類型,int *a=b 應該理解成*a是int型量即推出a是指向int的指針,將a初始化為b的地址。而不是腦補出 int** int**** 類型。


我覺得應該不存在int*或int**這種類型吧,比如定義兩個指針不能int*a,b;而只能int*a,*b;一樣。

------------原答案------------------------------------------

我一直理解為*符號是把變數當地址取值,int*a就是*a=int,意思就是按a的值做地址取到的是int類型,int**a就可以理解為按a的值做地址取到的值再次作為地址取值就取到了int。


指針通俗點講就是存放的地址,用來指向任何的地方,變數、結構體、類等等,所以指針在定義時需要明確指向的類型。類型加上一個*,int*指向int,int**指向int*,這就是一種約定,讀取時好明確佔用內存大小,便於取值。


不是說它們內存模型一樣就可以看做是同一個東西的

強類型語言中的類型設計是為了保證代碼無二義,可能會有些繁瑣,但是是必要的

如果你用int*來同時表示指向一個變數的指針和指向一個指針的指針,那麼當你從某處(比如形參)拿到一個int*類型的變數p時,*p=0修改的到底是一個變數的值還是一個指針的值?


你既然知道 int* 是指向 int 類型的對象的指針類型,那麼為什麼會不明白 int** 是指向 int* 類型的對象的指針類型呢?

至於你希望的無基礎類型的指針,C語言里不是提供了 void* 類型嘛。


去看彙編,不用全部看完,看完前幾節,就會對指針有更深的認識


#include "stdio.h"
int main(int argc , int **argv)
{
int a = 10;
int* b = a;
int* c = b;

printf(" a: %d
",a);
printf("*b: %d
",*b);
printf("*c: %d
",*c);
printf(" b: %d
",b);
printf("a: %d
",a);
printf(" c: %d
",c);

return 0;
}

輸出結果:

a: 10

*b: 10

*c: 2686776

b: 2686776

a: 2686776

c: 2686772

可以看到*c , b ,a的值是一樣的,所以題主應該明白為什麼了


你理解錯了,一顆星代表一次存儲器訪問,如果你c=b,那麼*c訪問一次存儲器只能拿到b,想拿到a,只能再加一顆星星**c。所以編譯器就是為了防止題主這樣理解錯誤的人,所以編譯出錯,要是編譯通過了是達不到題主預期的。

不加星星就不是存儲器訪問,如int a,這個a一般在寄存器里,不需要加星星就能訪問到。


你這個思路可以引申,int a int *b a b都是變數,為什麼b前面需要一個* int a char b a b都是變數,為什麼需要申明不同的類型,按照這個思路,所有編程語言都可以大一統了


當你修改(*b)的時候,其實改變的是a的值,當你修改(*c)的時候,修改的只是b,或者說是修改了b指向哪裡。要間接修改a就要用**c,所以int*和int**是有區別的。


int a;

auto b = a;

auto c = b;

使用auto代替煩人的類型。指針的含義其他答主都講了,我就不講了。


推薦閱讀:

C語言如何封裝printf函數?
C語言結構體內部的函數指針有什麼意義?
一條C語言語句不一定是原子操作,但是一個彙編指令是原子操作嗎?
c語言函數是如何獲取傳入的數組(指針)的指針所指向內容的長度的,有辦法嗎?
C語言零基礎想要自學,有什麼書可以推薦一下嗎?

TAG:C編程語言 | CC |