2017-10-10 63 views
-1

我試圖理解的代碼片段在 free a double pointer如何使用指針的指針用C

Why use double pointer? or Why use pointers to pointers?

我想了解以下之間的差別來分配內存。這兩個片段是從上面的網址

int** pt; 

pt = (int*) malloc(sizeof(int)*10); 

*pt = (int*) malloc(sizeof(int)*10); 

你可以用一些例子詳細說明和圖紙

+1

請不要指指針到指針作爲「雙指針」。後者聽起來太像是一個指向雙的指針,這是一個完全不同的動物。 –

+0

不要在C++中使用malloc。 – 2017-10-10 17:05:00

+0

@PeteBecker固定。感謝您的建議 – user2979872

回答

1

首先,該代碼段是壞的有以下幾個原因 - 第一是鑄造的malloc結果到了錯誤的類型,並使用了錯誤類型來計算的內存量。固定演員和類型的問題,我們有:

int **pt; 

pt = malloc(sizeof *pt * 10); // allocate space for 10 int * 
*pt = malloc(sizeof **pt * 10); // allocate space for 10 int 

執行第一行後,您具備以下條件:

 int **    int * 
    +---+     +---+ 
pt: | | --------------->| | pt[0] 
    +---+     +---+  
          | | pt[1] 
          +---+ 
          | | pt[2] 
          +---+ 
          ... 
          +---+ 
          | | pt[9] 
          +---+ 

你預留空間10個int *對象,pt點第一個。

下一行

*pt = malloc(sizeof **pt * 10); // allocate space for 10 int 

10個int對象分配空間,並設置pt[0]指向它們:

 int **    int *    int 
    +---+     +---+    +---+ 
pt: | | --------------->| | pt[0] -------->| | pt[0][0] 
    +---+     +---+    +---+ 
          | | pt[1]   | | pt[0][1] 
          +---+    +---+ 
          | | pt[2]   | | pt[0][2] 
          +---+    +---+ 
          ...     ... 
          +---+    +---+ 
          | | pt[9]   | | pt[0][9] 
          +---+    +---+ 

這說明分配 「鋸齒狀」 陣列的一種方式;您仍然可以將其索引爲pt[i][j],但與真正的2D數組不同,這些行在內存中不相鄰,並且每行的長度可能不同。通常你會寫,隨着

pt = malloc(sizeof *pt * ROWS); 
if (pt) 
{ 
    for (size_t r = 0; r < ROWS; r++) 
    { 
    pt[r] = malloc(sizeof *pt[r] * COLS); 
    } 
} 

當這一切都完成後,你有這樣的事情:

 int **   int *     int 
    +---+   +---+     +---+---+  +---+ 
pt: | | ---------> | | pt[0] --------> | | | ... | | pt[0][0] - pt[0][COLS-1] 
    +---+   +---+     +---+---+  +---+ 
        | | pt[1] ------+ 
        +---+    | +---+---+  +---+ 
        | | pt[2] ---+ +-> | | | ... | | pt[1][0] - pt[1][COLS-1] 
        +---+   |  +---+---+  +---+ 
         ...   | 
            |  +---+---+  +---+ 
            +----> | | | ... | | pt[2][0] - pt[2][COLS-1] 
              +---+---+  +---+ 
+0

結果的轉換是一個非常好的主意,因爲它允許編譯器爲此語句發出消息pt =(int *)malloc(sizeof(int)* 10); –

+0

@VladfromMoscow:使用'sizeof * pt'而不是'sizeof(int)'消除了這個特殊問題。 'T * p = malloc(sizeof * p * N);''無論'T'如何,總會做正確的事情。 –

+0

你確定嗎?考慮例如char * p = malloc(sizeof(struct A));和char * p = malloc(sizeof * p); –

2

以下是錯誤的,編譯器應該抱怨類型:

int** pt;  
pt = (int*) malloc(sizeof(int)*10); 

這也是錯誤的另一個原因(這裏pt不實際LLY指向任何可用):

int** pt;  
*pt = (int*) malloc(sizeof(int)*10); 

一個指向T是可以包含可以包含T類型的元素一些存儲器的地址T *類型的變量:

+------+ 
|  | pointer to T 
+------+ 
    | 
    v 
+-------------+-------------+-------------+ 
|    |    |    | elements of type T 
+-------------+-------------+-------------+ 

例如,在C,獲得什麼畫,你可以寫:

int *pi; 
pi = malloc(sizeof(int)*3); 

如果你有一個指針指向T然後再圖可能是這樣的:

+------+ 
|  | pointer to pointer to T 
+------+ 
    | 
    v 
+------+------+------+ 
|  |  |  | pointers to T 
+------+------+------+ 
    |  |  |  +-------------+-------------+-------------+ 
    |  |  +---->|    |    |    | elements of type T 
    |  |   +-------------+-------------+-------------+ 
    |  |  +-------------+-------------+ 
    |  +---->|    |    | elements of type T 
    |   +-------------+-------------+ 
    | 
    v 
+-------------+-------------+-------------+-------------+ 
|    |    |    |    | elements of type T 
+-------------+-------------+-------------+-------------+ 

和代碼可能是:

int **ppi; 
ppi = malloc(sizeof(int)*3); 
ppi[0] = malloc(sizeof(int)*3); 
ppi[1] = malloc(sizeof(int)*2); 
ppi[2] = malloc(sizeof(int)*4); 

當然,malloc可能會失敗,並返回值應該對故障進行測試。

1

的澆注你幫助編譯器發現錯誤在此代碼段

int** pt; 

pt = (int*) malloc(sizeof(int)*10); 

例如錯誤消息可以像

error: assignment from incompatible pointer type [-Werror=incompatible-pointer-types] 
    pt = (int*) malloc(sizeof(int)*10); 
    ^

沒有鑄造,編譯器可以接受這個明顯無效的代碼,因爲函數malloc的返回類型是void *,並且類型void *的指針可能被分配給任何其他類型的對象的指針。

即在所評估的表達式的類型int *轉讓的右側,而在分配的左側有類型int **的目的和有從類型int *的隱式轉換的類型int **

這段代碼

int** pt; 

*pt = (int*) malloc(sizeof(int)*10); 

是另一個原因無效。指針pt未通過對象的有效地址進行初始化。如果指針具有自動存儲持續時間,則它具有不確定的值,如果指針具有靜態存儲持續時間,則它具有NULL。無論如何,其解引用導致未定義的行爲。

因此,這將是正確的寫

int* pt; 
^^^^^^^ 
pt = (int*) malloc(sizeof(int)*10); 

但是這種結構

int** pt; 

//... 

*pt = (int*) malloc(sizeof(int)*10); 

可以在某些範圍內有效。

讓我們假設你聲明的指針

int *pt; 

,並希望在一個函數來初始化它。在這種情況下,您必須通過引用將指針傳遞給函數。否則,函數將處理指針的副本,在這種情況下,原始指針不會被分配到函數中。

所以相應的代碼片段可以看,因爲它是在示範程序中顯示

#include <stdlib.h> 
#include <stdio.h> 

size_t f(int **pt) 
{ 
    const size_t N = 10; 

    *pt = (int*) malloc(sizeof(int) * N); 

    if (*pt) 
    { 
     int value = 0; 
     for (size_t i = 0; i < N; i++) (*pt)[i] = value++; 
    } 

    return *pt == NULL ? 0 : N; 
} 

int main(void) 
{ 
    int *pt; 

    size_t n = f(&pt); 

    if (n) 
    { 
     for (size_t i = 0; i < n; i++) printf("%d ", pt[i]); 
     putchar('\n'); 
    } 

    free(pt); 
} 

程序輸出是

0 1 2 3 4 5 6 7 8 9 
+0

不能從main()傳遞指針給f(),如f(pt)。如果不是,爲什麼不呢? – user2979872

+0

@ user2979872事實上,我描述了爲什麼我的答案是不可能的。你能詳細說明什麼不清楚嗎? –

+0

不清楚的是f(pt)和f(&pt)之間的差異。當你將pt聲明爲int * pt時,我想知道pt和&pt之間有什麼區別。如果我理解正確,&pt意味着pt和pt的地址指向pointee的地址(只有當像pt =&a那樣指定a被聲明爲int a = 5時)。否則,pt指向沒有有效的地址。請糾正我,如果我錯了 – user2979872