我想结束与char **
当一次创建一个字符数组(字符串)数组时,char **
实际上是如何实现的呢?
我得到char *
是一个指向char的指针,char *array[]
是一个char指针数组,但是char **
到底做了什么以及它是如何做的?
当我听到dereferences这个词的时候,它让我觉得指针被删除了指针取消引用究竟是什么意思?更改指针指向的值?
由于
答案 0 :(得分:13)
“解除引用”指针意味着访问指针指向的值。假设以下声明:
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]
,我们为LENGTH + 1个元素分配一个足够大的内存块,每个元素大到足以容纳char
值(sizeof *arr[i] == sizeof (char)
,因为{的类型{ {1}};请注意*arr[i] == char
始终为1)并将结果分配给sizeof (char)
。
由于我们为每个字符串分配了一个单独的arr[i]
调用,因此它们在内存中不太可能是连续的。这是另一个显示上述代码的可能结果的内存映射:
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?? ...
答案 1 :(得分:9)
指针是一种保存值的地址的类型,而不是保存实际值。
因此,在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的值,这是某个字符。
答案 2 :(得分:3)
取消引用指针意味着访问指针指向的值。例如,
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的指针的原因是指针允许您访问多于1个值。举个例子:
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的原因。一个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;
}
答案 3 :(得分:3)
图表值1000字。看看here
char,char *和char **只是描述变量(内存区域)包含内容的类型。
使用类似*变量的解除引用实际上表示将变量中的值视为内存地址并实际返回该地址的值。这是间接的。
**变量只是两个间接级别。即,变量中的值是将返回的数据的另一个存储器地址的存储器地址。
地址通常来自运算符地址&
或来自内存分配函数/运算符,如new