我已经读了几天学习艰难之路,但这里有一些我想要真正理解的东西。作者Zed写道,char **
是指向#(指向char的指针)"并且说这是必要的,因为我试图指向某事2维。
以下是在网页上写的内容
char *已经是"指向char"的指针,因此它只是一个字符串。然而,你需要2个级别,因为名称是2维的,这意味着你需要char **指向(指向char的指针)"类型。
这是否意味着我必须使用一个可以指向二维的变量,这就是为什么我需要两个**
?
只需一点点跟进,这也适用于n维吗?
这是相关代码
char *names[] = { "Alan", "Frank", "Mary", "John", "Lisa" };
char **cur_name = names;
答案 0 :(得分:24)
不,该教程质量有问题。我不建议继续阅读。
char**
是指向指针的指针。它不是2D阵列。
它不是指向数组的指针。
它不是指向2D数组的指针。
本教程的作者可能会感到困惑,因为有一个广泛的错误和不正确的做法,说你应该像这样分配动态2D数组:
// BAD! Do not do like this!
int** heap_fiasco;
heap_fiasco = malloc(X * sizeof(int*));
for(int x=0; x<X; x++)
{
heap_fiasco[x] = malloc(Y * sizeof(int));
}
然而,这不是2D数组,它是在整个堆中分配的缓慢,碎片化的查找表。访问查找表heap_fiasco[x][y]
中的一个项的语法看起来就像数组索引语法一样,因此很多人出于某种原因相信这是你分配二维数组的方式。
动态分配2D数组的正确方法是:
// correct
int (*array2d)[Y] = malloc(sizeof(int[X][Y]));
您可以告诉第一个不是数组,因为如果执行memcpy(heap_fiasco, heap_fiasco2, sizeof(int[X][Y]))
,代码将崩溃并刻录。这些项目未在相邻内存中分配。
同样memcpy(heap_fiasco, heap_fiasco2, sizeof(*heap_fiasco))
也会崩溃和刻录,但由于其他原因:你得到的指针大小不是数组。
虽然memcpy(array2d, array2d_2, sizeof(*array2d))
可行,但因为它是2D数组。
答案 1 :(得分:6)
指针花了一些时间来理解。我强烈建议绘制图表。
请阅读并理解this part of the C++ tutorial(至少关于图表确实帮助我的指针)。
告诉你需要一个指向二维数组char的指针的谎言。你不需要它,但它是一种方法。
记忆是连续的。如果你想在单词 hello 中连续放置5个字符(字母),你可以定义5个变量并始终记住使用它们的顺序,但是当你想要保存单词时会发生什么有6个字母?你定义了更多变量吗?如果你只是按顺序将它们存储在内存中会不会更容易?
所以你要做的是你向操作系统询问5个字符(并且每个字符恰好是一个字节),然后系统返回一个存储器地址,其中5个字符序列开始。你把这个地址存储在一个我们称之为指针的变量中,因为它指向你的记忆。
指针的问题在于它们只是地址。你怎么知道那个地址存储的是什么?它是5个字符还是8字节的大二进制数?或者它是您加载的文件的一部分?你怎么知道的?
这是像C这样的编程语言试图通过为您提供类型来帮助的地方。类型告诉你变量存储的内容和指针也有类型,但它们的类型告诉你指针指向的是什么。因此,char *
是指向内存位置的指针,该内存位置包含单个char
或chars
序列。可悲的是,有关char
多少1 2 3 4
5 6 7 8
9 10 11 12
的部分你需要记住自己。通常,您将该信息存储在一个变量中,以便提醒您有多少个字符。
所以当你想要一个二维数据结构时,你如何表示它?
最好用一个例子来解释。我们做一个矩阵:
1 2 3 4
它有4列3行。我们如何储存?
好吧,我们可以制作3个序列,每个序列包含4个数字。第一个序列是5 6 7 8
,第二个序列是9 10 11 12
,第三个和最后一个序列是1 2 3 4
5 6 7 8
9 10 11 12
。因此,如果我们想要存储4个数字,我们将要求系统为我们保留4个数字并给我们指向它们的指针。这些将是指向数字的指针。但是,由于我们需要其中的3个,我们将要求系统向指针数字提供3个指针。
这就是你最终得到的建议解决方案......
另一种方法是认识到您需要4次3个数字,并且只需要系统将12个数字存储在一个序列中。但那么如何访问第2行和第3列中的数字呢?这是数学的用武之地,但让我们在我们的例子中尝试:
offset from start: 0 1 2 3 4 5 6 7 8 9 10 11
numbers in memory: [1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]
如果我们将它们彼此相邻存储,它们将如下所示:
row | column | offset | value
1 | 1 | 0 | 1
1 | 2 | 1 | 2
1 | 3 | 2 | 3
1 | 4 | 3 | 4
2 | 1 | 4 | 5
2 | 2 | 5 | 6
2 | 3 | 6 | 7
2 | 4 | 7 | 8
3 | 1 | 8 | 9
3 | 2 | 9 | 10
3 | 3 | 10 | 11
3 | 4 | 11 | 12
所以我们的映射是这样的:
offset = (row - 1) * 4 + (column - 1)
我们现在需要制定一个简单的公式,将行和列转换为偏移...当我有更多时间时,我会回到它...现在我需要回家(遗憾)...
编辑:我有点晚了,但让我继续。要查找行和列中每个数字的偏移量,可以使用以下公式:-1
如果你注意到这两个ensuring
并想一想,你会明白这是因为我们的行和列编号从1开始,我们必须这样做,这就是为什么计算机科学家更喜欢基于零的偏移(因为这个公式)。但是使用C中的指针时,语言本身会在您使用多维数组时为您应用此公式。因此,这是另一种方式。
答案 2 :(得分:1)
从你的问题我理解的是,你在问为什么你需要char **作为声明为* names []的变量。所以答案是当你只是简单地写名字[]时,它就是数组和数组的语法基本上是一个指针。
因此,当您编写* names []时,这意味着您指向一个数组。并且因为数组基本上是一个指针,所以这意味着你有一个指针指针,这就是为什么编译器不会抱怨如果你写
char ** cur_name =姓名;
在上面的行中,您将声明一个指向字符指针的指针,然后使用指向数组的指针初始化它(记住数组也是指针)。