2009-04-17 113 views
37

什麼時候使用任何語言的指針都需要有人使用多個指針,比方說一個三重指針。什麼時候使用三指針而不是僅使用常規指針有意義?用於多級指針取消引用?

例如:

char * * *ptr; 

代替

char *ptr; 
+19

這是一個非常_indirect_的問題。 – 2010-07-23 20:39:46

+5

要成爲http://c2.com/cgi/wiki?ThreeStarProgrammer – 2011-06-14 22:28:10

+0

您可能會喜歡[cdecl](http://cdecl.org/) – bobobobo 2011-08-17 23:05:20

回答

57

每個星應當被理解爲這樣

char *foo; 

是「字符,其由指針FOO指向」「其由指針指向」。但是,

char *** foo; 

是「指向指向指向foo的指針的指針指向的char」。因此foo是一個指針。在那個地址是第二個指針。在指向的地址是第三個指針。解引用第三個指針會導致char。如果這就是它的全部,那麼很難做出這樣的事情。儘管如此,它仍然有可能完成一些有用的工作。想象一下,我們正在編寫一個bash替代品或其他一些過程控制程序。我們希望以面向對象的方式管理我們的流程調用...

struct invocation { 
    char* command; // command to invoke the subprocess 
    char* path; // path to executable 
    char** env; // environment variables passed to the subprocess 
    ... 
} 

但我們想要做一些奇特的事情。我們希望有一種方法可以瀏覽每個子流程看到的所有不同環境變量集。要做到這一點,我們收集每一套從調用實例env成員到一個數組env_list並把它傳遞到以該交易的功能:

void browse_env(size_t envc, char*** env_list); 
4

N維動態分配的陣列,其中N> 3,需要在C.

+0

此外,當將二維數組傳遞給將修改它。如果你有一個double ** ptr,你可以將它作爲fn(&ptr)傳遞,函數fn()將有參數fn(double *** ptr)。 – 2009-04-17 01:29:51

+0

這是一個有點誤導,因爲myarray [0] [1] [2]通常不是實際上3個指針間接(雖然它可能)。內存通常是連續的,編譯器爲你做算術。 – 2009-04-17 01:50:10

+0

上述編輯是否解決您的疑慮?也就是說,我們可以同意動態分配的3維數組需要解引用3+指針嗎? – 2009-04-17 02:00:05

1
int main(int argc, char** argv); 
三個或更多個間接
3

雙指針的標準用法,例如:myStruct ** ptrptr,作爲指針的指針。例如,作爲函數參數,這允許您更改調用方指向的實際結構,而不是僅能夠更改該結構中的值。

0

如果您必須修改函數內的指針,您必須傳遞對它的引用。

5

指針只是一個保存內存地址的變量。

因此,當你想要保存一個指針變量的地址時,你使用了一個指針指針。

如果你想返回一個指針,並且你已經使用返回變量的東西,你會傳入一個指針的地址。然後該函數將該指針取消引用,以便它可以設置指針值。即該函數的參數將是指向指針的指針。

多級間接也用於多維數組。如果你想返回一個二維數組,你可以使用一個三重指針。在使用它們進行多維數組時,儘管要仔細地在每個間接層次上進行適當的轉換。

下面是通過參數返回一個指針值的例子:

//Not a very useful example, but shows what I mean... 
bool getOffsetBy3Pointer(const char *pInput, char **pOutput) 
{ 
    *pOutput = pInput + 3; 
    return true; 
} 

你調用這個函數像這樣:

const char *p = "hi you"; 
char *pYou; 
bool bSuccess = getOffsetBy3Pointer(p, &pYou); 
assert(!stricmp(pYou, "you")); 
0

這是有道理的使用指針的指針,每當指針實際上指向一個指針(這個鏈是無限的,因此「三指針」等是可能的)。

創建此類代碼的原因是因爲您希望編譯器/解釋器能夠正確檢查您正在使用的類型(防止神祕錯誤)。

您不必使用這種類型 - 只要您需要實際取消引用指針並訪問指針指向的數據,您總是可以簡單地使用簡單的「void *」和類型轉換。但這通常是不好的做法,容易出錯 - 當然有些情況下使用void *實際上是好的,並且使代碼更加優雅。想想它更像你的最後一招。

=>它主要用於幫助編譯器確保事物按照它們應該被使用的方式使用。

1

函數,它們封裝資源的創作經常使用雙指針。也就是說,你傳遞一個指向資源的地址。該函數然後可以創建有問題的資源,並將指針設置爲指向它。這是唯一可能的,如果它有問題的指針的地址,所以它必須是一個雙指針。

0

說實話,我很少見過三重指針。

我瞥了一下google代碼搜索,還有someexamples,但不是很明顯。 (見末尾的鏈接 - SO不喜歡它們)

正如其他人所提到的,你會不時看到雙指針。普通的單指針是有用的,因爲它們指向一些分配的資源。雙指針很有用,因爲您可以將它們傳遞給一個函數,並使函數爲您填充「普通」指針。

這聽起來像也許你需要一些關於什麼指針和它們是如何工作的解釋? 如果你還沒有,你首先需要明白。

但是,這是一個separatequestion(:

http://www.google.com/codesearch/p?hl=en#e_ObwTAVPyo/security/nss/lib/ckfw/capi/ckcapi.h&q=***%20lang:c&l=301

http://www.google.com/codesearch/p?hl=en#eVvq2YWVpsY/openssl-0.9.8e/crypto/ec/ec_mult.c&q=***%20lang:c&l=344

2

您可以使用額外的間接水平 - 或指向 - 在必要的時候,不是因爲它會很有趣你很少看到三倍。指針;我不認爲我曾經見過一個四重指針(如果我這樣做,我的腦海裏會浮現)

狀態表可以用一個二維數組來表示適當的數據類型(例如指向結構的指針)。當我編寫一些幾乎泛型的代碼來執行狀態表時,我記得有一個函數帶了一個三重指針 - 它代表了一個指向結構的二維數組。哎喲!

0

C++中很少使用指向指針的指針。它們主要有兩種用途。

第一個用途是傳遞一個數組。例如,char**是指向char的指針,它通常用於傳遞字符串數組。指向數組的指針不能正常工作,但這是一個不同的主題(如果您想了解更多信息,請參閱comp.lang.c FAQ)。在極少數情況下,您可能會看到第三個*用於陣列數組,但將所有內容存儲在一個連續數組中並手動將其索引(例如array[x+y*width]而不是array[x][y])會更有效。然而,在C++中,由於容器類的原因,這種情況非常少見。

第二種用途是通過引用。參數int*允許函數修改調用函數指向的整數,並且通常用於提供多個返回值。這種通過引用傳遞參數以允許多個返回的模式仍然存在於C++中,但是與其他使用傳遞引用一樣,它通常被引入實際引用所取代。通過引用傳遞的另一個原因 - 避免複雜的構造 - 也可以用C++引用。

C++有第三個因素減少多個指針的使用:它有string。對字符串的引用可能採用C語言中的char**類型,以便該函數可以更改它傳遞的字符串變量的地址,但在C++中,我們通常會看到string&

0

當您使用嵌套動態分配(或指針鏈接)的數據結構。這些東西都是通過指針鏈接的。

3

Char *** foo可以解釋爲指向二維字符串數組的指針。

4

ImageMagicks魔杖有被聲明爲

WandExport char* * * * * * DrawGetVectorGraphics ( const DrawingWand *) 

I am not making this up功能。如果你想創建一個對象

struct customer { 
    char *name; 
    char *address; 
    int id; 
} typedef Customer; 

,你會做這樣的事情:

10

如果您在C「對象」的工作,你可能有這樣的

Customer *customer = malloc(sizeof Customer); 
// Initialise state. 

我們」因爲struct參數是按值傳遞的,所以我們需要使用一個對象。 (另外:Objective-C中,對於C面向對象的包裝器語言,使用內部但明顯指針struct峯)

如果我需要存儲多個對象,我使用的數組:

Customer **customers = malloc(sizeof(Customer *) * 10); 
int customerCount = 0; 

由於C 中的數組變量指向第一個項目,所以我再次使用指針...。現在我有雙重指針。

但現在想象我有一個過濾數組並返回一個新的函數。但想象它不能通過返回機制,因爲它必須返回一個錯誤代碼 - 我的函數訪問數據庫。我需要通過一個引用參數來做到這一點。這是我的函數的簽名:

int filterRegisteredCustomers(Customer **unfilteredCustomers, Customer ***filteredCustomers, int unfilteredCount, int *filteredCount); 

該函數接受客戶的數組,並返回一個參考給客戶(這是指向一個struct)的陣列。它還需要客戶的數量並返回過濾客戶的數量(再次,通過引用參數)。

我可以這樣調用它:

Customer **result, int n = 0; 
int errorCode = filterRegisteredCustomers(customers, &result, customerCount, &n); 

我可以去想象更多的情況......這個人是沒有typedef

int fetchCustomerMatrix(struct customer ****outMatrix, int *rows, int *columns); 

很顯然,我會成爲一個可怕的和/或虐待狂的開發者離開這種方式。因此,使用:

typedef Customer *CustomerArray; 
typedef CustomerArray *CustomerMatrix; 

我可以做到這一點:

int fetchCustomerMatrix(CustomerMatrix *outMatrix, int *rows, int *columns); 

如果您的應用在使用每級基質中的酒店使用,你可能需要一個數組的矩陣:

int fetchHotel(struct customer *****hotel, int *rows, int *columns, int *levels); 

或者只是這樣的:

typedef CustomerMatrix *Hotel; 
int fetchHotel(Hotel *hotel, int *rows, int *columns, int *levels); 

不要誤會我,甚至開始酒店的陣列上:(?某種大型酒店公司的)

int fetchHotels(struct customer ******hotels, int *rows, int *columns, int *levels, int *hotels); 

...排列成矩陣:

int fetchHotelMatrix(struct customer *******hotelMatrix, int *rows, int *columns, int *levels, int *hotelRows, int *hotelColumns); 

我想說是你可以想象瘋狂的應用程序的多個indirections。如果多指針是一個好主意,並且您決定使用它們,請確保您使用typedef

用C不積極地使用基於類型的別名分析的單線程方言特別(這是否交計數作爲一個SevenStarDeveloper申請?)

0

,它有時是有用的寫內存管理器這可以適應可重新定位的對象。應用程序不是直接將指針指向內存塊,而是接收指向處理描述符表的指針,每個指針都包含指向實際內存塊的指針以及指示其大小的單詞。如果人們需要一種用於struct woozle分配空間,可以說:

struct woozle **my_woozle = newHandle(sizeof struct woozle); 

,然後訪問(有點笨拙在C語法 - 語法是 帕斯卡清潔器):(* my_woozle) - > someField = 23 ;重要的是應用程序不是 保持直接指向任何句柄的目標,通過調用 分配內存的函數,但如果只有一個指針指向由句柄標識的每個塊 ,則內存管理器將能夠在 之間移動內容案件碎片將成爲一個問題。

的方法並不在C方言積極 追求型爲主的混疊工作近也,由於NewHandle返回的指針不 識別struct woozle*類型的指針,而是標識類型的指針 void*,甚至在那些指針類型將具有 相同表示的平臺上,標準並不要求實現 將指針轉換爲它應該預期可能發生別名 的指示。

0

雙重間接簡化了許多樹平衡算法,通常人們希望能夠有效地將子樹與其父級「斷開連接」。舉例來說,一個AVL樹的實現可能是:

void rotateLeft(struct tree **tree) { 
    struct tree *t = *tree, 
       *r = t->right, 
       *rl = r->left; 
    *tree = r; 
    r->left = t; 
    t->right = rl; 
} 

沒有「雙指針」,我們將不得不做一些更復雜,就像明確地跟蹤一個節點的父節點和它是否是一個左或右分支。