为什么我需要使用类型**指向类型*?

几天来,我一直在阅读“学习艰难的道路”,但这是我想要真正理解的东西。 作者Zed写道, char **用于指向(指向char的指针),并说这是必需的,因为我试图指向二维的东西。

这是在网页上写的内容

char *已经是“指向char的指针”,所以这只是一个字符串。 但是你需要2个级别,因为名称是2维的,这意味着你需要char **作为“指向(指向char的指针)”类型。

这是否意味着我必须使用一个可以指向二维的变量,这就是为什么我需要两个**

只是一点点跟进,这也适用于n维吗?

这是相关的代码

 char *names[] = { "Alan", "Frank", "Mary", "John", "Lisa" }; char **cur_name = names; 

不,那个教程质量有问题。 我不建议继续阅读它。

char**是指向指针的指针。 它不是2Darrays。 它不是指向数组的指针。 它不是指向2D数组的指针。

本教程的作者可能会感到困惑,因为有一个广泛的错误和不正确的做法,说你应该像这样分配动态2D数组:

 // BAD! Do not do like this! int** heap_fiasco; heap_fiasco = malloc(X * sizeof(int*)); for(int x=0; x 

然而,这不是2D数组,它是在整个堆中分配的缓慢,碎片化的查找表。 访问查找表中的一个项目heap_fiasco[x][y]语法看起来就像数组索引语法一样,因此很多人因为某种原因认为这是你分配2D数组的方式。

动态分配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数组。

指针花了一些时间来理解。 我强烈建议绘制图表。

请阅读并理解C ++教程的这一部分 (至少关于图表确实帮助我的指针)。

告诉你你需要一个指向二维数组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 

它有4列3行。 我们如何存储?

好吧,我们可以制作3个序列,每个4个数字。 第一个序列是1 2 3 4 ,第二个序列是5 6 7 8 ,第三个和最后一个序列是9 10 11 12 。 因此,如果我们想要存储4个数字,我们将要求系统为我们保留4个数字并给我们指向它们的指针。 这些将成为数字的指针 。 但是,由于我们需要有3个,我们会要求系统给出指针数字的 3个指针。

这就是你最终提出的建议解决方案……

另一种方法是认识到你需要4次3个数字,并且只需要系统将12个数字存储在一个序列中。 但是,您如何访问第2行和第3列中的数字? 这是数学的来源,但让我们在我们的例子中尝试:

 1 2 3 4 5 6 7 8 9 10 11 12 

如果我们将它们彼此相邻存储,它们将如下所示:

 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并想一想,你会明白这是因为我们的行和列编号从1开始,我们必须这样做,这就是为什么计算机科学家更喜欢基于零的偏移(因为这个公式)。 但是,使用C语言中的指针,当您使用多维数组时,语言本身会为您应用此公式。 因此,这是另一种方式。

从你的问题我理解的是你在问为什么你需要char **为变量声明为* names []。 所以答案是当你只是简单地写名字[]时,它就是数组和数组的语法基本上是一个指针。

因此,当您编写* names []时,这意味着您指向一个数组。 因为数组基本上是一个指针,所以这意味着你有一个指向指针的指针,这就是为什么编译器在你写的时候不会抱怨的原因

char ** cur_name = names;

在上面的行中,您将声明一个指向字符指针的指针,然后使用指向数组的指针初始化它(记住数组也是指针)。