【发布时间】:2011-02-15 13:34:37
【问题描述】:
我想结束我对char ** 的困惑
一旦创建了一个字符数组(字符串)数组,char ** 究竟是如何做到这一点的?
我知道char * 是一个指向字符的指针,而char *array[] 是一个字符指针数组,但是char ** 到底是做什么的以及它是如何做到的?
此外,当我听到取消引用这个词时,它让我认为指针已被删除,取消引用指针到底是什么意思?改变指针指向的值?
谢谢
【问题讨论】:
我想结束我对char ** 的困惑
一旦创建了一个字符数组(字符串)数组,char ** 究竟是如何做到这一点的?
我知道char * 是一个指向字符的指针,而char *array[] 是一个字符指针数组,但是char ** 到底是做什么的以及它是如何做到的?
此外,当我听到取消引用这个词时,它让我认为指针已被删除,取消引用指针到底是什么意思?改变指针指向的值?
谢谢
【问题讨论】:
“取消引用”指针意味着访问指针指向的值。假设以下声明:
int a = 10;
int *p = &a;
这是两个变量的假设内存映射:
项目地址 0x00 0x01 0x02 0x03 ---- -------- ---- ---- ---- ---- 一个 0x80001000 0x00 0x00 0x00 0x0A 0x80001004 0x80 0x00 0x10 0x00a 包含整数值 10。p 包含 a (0x80001000) 的地址。如果我们想通过p 访问a 的内容,我们取消引用 p 与间接运算符*。因此,表达式*p 等价于表达式a。如果我们写了
*p = 16;
这和写作是一样的
a = 16;
下面是一段简短的 sn-p 代码,展示了如何使用 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 *),并将生成的指针值分配给 @987654340 @。 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),因为类型为*arr[i] == char;注意@ 987654356@ 始终为 1) 并将结果分配给 arr[i]。
由于我们为每个字符串分配一个单独的malloc 调用,因此它们在内存中不太可能是连续的。这是另一个内存映射,显示了上述代码的可能结果:
【讨论】:
free(arr);就足够了吗?
指针是一种类型,它保存一个值的地址,而不是保存实际值。
因此,对于 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 处的值,这是某个字符。
【讨论】:
图表值 1000 字。看看here
char、char* 和 char** 只是描述变量(内存区域)所包含内容的类型。
使用像 *variable 这样的解引用实际上表示将变量中的值视为内存地址并实际返回该地址处的值。这是间接的。
**variable 只是两个间接级别。即变量中的值是将返回的数据的另一个内存地址的内存地址。
地址通常来自运算符的地址,& 或来自内存分配函数/运算符,如 new
【讨论】:
取消引用指针意味着访问指针指向的值。例如,
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)
请注意,第一个元素的 index 为 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;
}
【讨论】: