我想結束與char **
字符**和取消引用指針
我的困惑在一次創建字符數組(字符串)數組如何char **
真正做到這一點?
我知道char *
是一個指向char的指針,char *array[]
是一個char指針數組,但char **
究竟做了什麼,它是如何做到的?
另外,當我聽到這個詞的解除引用,這讓我覺得指針被刪除了什麼是取消引用指針意味着什麼?更改指針指向的值?
由於
我想結束與char **
字符**和取消引用指針
我的困惑在一次創建字符數組(字符串)數組如何char **
真正做到這一點?
我知道char *
是一個指向char的指針,char *array[]
是一個char指針數組,但char **
究竟做了什麼,它是如何做到的?
另外,當我聽到這個詞的解除引用,這讓我覺得指針被刪除了什麼是取消引用指針意味着什麼?更改指針指向的值?
由於
「解引用」一個指針裝置訪問所述值的指針指向。假定以下聲明:
int a = 10;
int *p = &a;
下面是兩個變量的假想存儲器映射:
Item Address 0x00 0x01 0x02 0x03 ---- ------- ---- ---- ---- ---- a 0x80001000 0x00 0x00 0x00 0x0A p 0x80001004 0x80 0x00 0x10 0x00
a
包含整數值10 p
包含a
(0x80001000)中的地址。如果我們想要訪問a
到p
的內容,我們使用間接運算符*
對解除引用p
。因此,表達*p
等效於表達a
。如果我們寫
*p = 16;
這是相同的書面
a = 16;
這裏的代碼展示瞭如何使用char **
類型的對象來創建一個字符串數組一小片段:
#include <stdlib.h>
#define N 20 // For this example, we will allocate 20 strings
#define LENGTH 10 // of 10 characters each (not counting 0 terminator)
...
char **arr = malloc(sizeof *arr * N);
if (arr)
{
size_t i;
for (i = 0; i < N; i++)
{
arr[i] = malloc(sizeof *arr[i] * (LENGTH + 1));
strcpy(arr[i], " ");
}
}
經歷它一行行,
char **arr = malloc(sizeof *arr * N);
分配N個元素的塊,每個元素的大小足以存儲指向char的指針(sizeof *arr
== sizeof (char *)
,因爲類型爲*arr
== char *
),並將結果指針值指定爲arr
。 IOW,arr
指向第一指針char
,因此類型char **
。請注意,如果分開申報和函數調用,它看起來像
char **arr;
...
arr = malloc(sizeof *arr * N);
我們想要的malloc
結果分配給arr
,沒有什麼arr
點。
if (arr)
malloc
可能會失敗,所以我們希望在使用它之前檢查結果。在事件malloc
失敗,它會返回一個NULL指針值。
{
size_t i;
for (i = 0; i < N; i++)
{
arr[i] = malloc(sizeof *arr[i] * (LENGTH + 1));
對於每個字符指針arr[i]
,我們分配的存儲器足夠大的長度+ 1個元素,每個大到足以容納一個char
值的塊(sizeof *arr[i] == sizeof (char)
,由於類型的*arr[i] == char
;請注意,sizeof (char)
始終爲1)並將結果分配給arr[i]
。
由於我們爲每個字符串分配了一個單獨的malloc
調用,所以它們不太可能在內存中連續存在。下面是表示上述代碼的可能結果另一個存儲器映射:「 * P1 == C,即解除引用P1允許我們從讀/寫至c」
Item Address 0x00 0x01 0x02 0x03 ---- ------- ---- ---- ---- ---- arr 0x80001000 0xA0 0xCC 0x00 0x00 ... arr[0] 0xA0CC0000 0xA0 0xCC 0x20 0x00 arr[1] 0xA0CC0004 0xA0 0xCC 0x20 0x40 arr[2] 0xA0CC0008 0xA0 0xCC 0x21 0x28 ... arr[19] 0xA0CC0014 0xA0 0xCC 0x23 0x10 ... arr[0][0] 0xA0CC2000 ' ' ' ' ' ' ' ' arr[0][4] 0xA0CC2004 ' ' ' ' ' ' ' ' arr[0][8] 0xA0CC2008 ' ' ' ' 0x00 0x?? ... arr[1][0] 0xA0CC2040 ' ' ' ' ' ' ' ' arr[1][4] 0xA0CC2044 ' ' ' ' ' ' ' ' arr[1][8] 0xA0CC2048 ' ' ' ' 0x00 0x?? ...
解引用一個指針裝置訪問所述值的指針點到。例如,
char c = 'c'; // This is a primitive value. You cannot dereference it.
char* p1 = &c; // A pointer to the address of c
char** p2 = &p1; // A pointer to the address of p1
/* Now, the following is true:
*p1 == c, i.e. dereferencing p1 allows us to read from/write to c.
*p2 == p1
**p2 == *(*p2) == *p1 = c - dereferencing p2 twice is c, too */
您使用指向c而不是c的指針的原因是指針允許您訪問多個值。以此爲例:
char[4] str;
char c0 = 'a', c1 = 'b', c3 = 'c', c4 = '\0';
str[0] = c0; str[1] = c1; str[2] = c2; str[3] = c3;
str = "abc"; // Same as the above line
現在假設我們需要第二個字符。我們可以通過c1
訪問它。但正如你所看到的,這個符號非常麻煩。另外,如果我們從文件中讀取字符串而不是寫入字符串,我們就不得不做很複雜的事情。相反,我們只是寫
str[1] /* or */ *(str+1)
注意,第一個元素的指數 0,第二個1 - 這就是爲什麼我們在這裏使用了1。一個char**
把這個變成了十一個 - 我們有一個數組字符。假設我們有這樣一個數組,我們稱之爲input
,並且需要找出它中所有字符串的長度。這是我們如何做到這一點:
int lensum(char **input) {
int res = 0;
while (*input) { // This loops as long as the value input points to is not 0.
char* p = *input; // Make a copy of the value input currently points to
while (*p != '\0') { // Loop while the copy does not point to a char '\0'
res += 1; // We found a character
p++; // Check next character in the next iteration
}
input++; // Check next string in the next iteration
}
return res;
}
指針是一個保存了地址的值,而不是控股的實際值的類型。
因此,在char * p的情況下,一旦分配,p將包含地址A.解除引用該指針表示訪問存儲在地址A的值。可以將字符串存儲在char *中的原因是分配的內存是連續的。所以,A是存儲第一個字符的地址,A + 1是存儲第二個字符的地址等等。
在char ** pp的情況下,它存儲char *的地址。調用這個地址B.所以,取消引用pp意味着訪問地址B的值,恰好是一個char *,恰好持有一個字符串。以相同的方式,B + 1(實際上是B + sizeof(char *))存儲下一個值,這是另一個字符串。
解引用pp兩次(即** pp)表示您首先訪問地址B處的值(例如A),然後再次解除引用,以獲取地址A處的值,該值是某個字符。
真的很好的解釋。謝謝。 – jarryd
對不起,我是一個新手,所以我可能是錯的,但要完全清理分配後,你將不得不遍歷所有字符串和自由arr [我]以及釋放整個數組(arr),是嗎?或者僅僅是「自由(arr)」就足夠了? –