我按照以下教程进行操作:http://www.learn-c.org/en/Arrays_and_Pointers
在他们的代码中:
// Allocate memory for nrows pointers
char **pvowels = (char **) malloc(nrows * sizeof(char));
// For each row, allocate memory for ncols elements
pvowels[0] = (char *) malloc(ncols * sizeof(char));
pvowels[1] = (char *) malloc(ncols * sizeof(char));
但如果我删除(char *)和(char **)它仍然有效:
// Allocate memory for nrows pointers
char **pvowels = malloc(nrows * sizeof(char));
// For each row, allocate memory for ncols elements
pvowels[0] = malloc(ncols * sizeof(char));
pvowels[1] = malloc(ncols * sizeof(char));
本教程没有解释这是什么,我假设它是某种类型的类型转换,也许对于这个例子来说实际输入它是不重要的,而作者认为显式键入强制转换非常重要。
另外,我不熟悉双重' *'是(**)。
只是想确定我没有遗漏任何东西,谢谢你的任何解释。
答案 0 :(得分:2)
源代码中的malloc函数是为您的目的分配特定数据类型所需的内存量(char变量),返回过去必须进行类型转换但不再是,它仍然是典型的预防措施。 / p>
c++
是指向char类型变量的指针。 a char *
是指向char类型变量的指针。
我不知道指针已经向你解释了多少,但你可以把char指针想象成char变量列表中引用的第一个char。这就是字符串在内存中的设置方式,字符串只是char变量的集合。
如果你更深层次,你可以有一个char指针指针,它指向字符串列表中引用的第一个字符串。这就是作者在这里所做的,他正在为一个char指针数组分配所需的内存,并且由于这个数组中的每个char指针本身都指向一组char值,因此它们每个都必须为它们分配内存。
编辑:更具体地说,这些事物的“列表”称为数组。 char数组可以被认为是一个字符串,char数组的数组可以被认为是一个句子。
答案 1 :(得分:2)
在C语言的原始K& R定义(1989年之前)中,*alloc
函数返回char *
。由于该语言没有(并且仍然没有)允许在不兼容的指针类型之间进行分配,因此如果该类型不是char *
,则必须将结果显式地转换为目标类型:
int *p;
...
p = (int *) malloc( n * sizeof (int) );
1989标准引入了void *
类型,这是上述分配规则的一个例外,并作为"泛型"指针类型 - 值void *
可以分配给任何指针类型而无需显式强制转换,因此从C89标准开始,
p = malloc( n * sizeof (int) );
会奏效。
请注意,在C ++中这是 not true - 在该语言中,必须将void *
值强制转换为目标类型,因此在C ++中您仍然需要编写
p = (int *) malloc( n * sizeof (int) );
但如果您正在编写C ++,那么您无论如何都不应该使用malloc
。
在我们大多数人中,将malloc
的结果视为错误。根据C89版本的语言,您实际上可以通过引入一个bug
这样做。在该版本下,如果编译器在范围内没有声明的情况下看到函数调用,它将假定函数返回int
。所以,如果你以某种方式忘记包括stdlib.h
并写了
int *p = (int *) malloc( n * sizeof (int) );
编译器会假设malloc
返回int
。没有演员表,这将导致"不兼容的类型分配"诊断;然而,包括演员阵容会抑制诊断,在你遇到(有时非常微妙且难以诊断)运行时问题之前,你不会意识到存在问题。
不再是这种情况 - 语言的C99版本取消了隐式int
声明,因此如果您忘记包含stdlib.h
,则可以获得诊断。
然而,我们中的许多人认为演员阵容增加了不必要的维护负担(如果你改变目标指针的类型,你也必须更新演员阵容),而IMO会让代码更难阅读。
为什么这种做法仍然存在于C?有几个原因:
就我个人而言,我认为正确撰写*alloc
来电的方式是
T *p = malloc( n * sizeof *p );
T *p = calloc( n, sizeof *p );
T *tmp = realloc( p, n * sizeof *p );
清理,如果您更改了p
的类型,则无需更改*alloc
来电本身的任何内容。
修改强>
另外,我不熟悉双重' *'是(**)。
在C中可以实现多个间接 - 您可以指向指针,指向指针的指针等。例如,您的pvowels
对象指向指向char
的指针序列中的第一个:
char ** char * char
------- ------- ----
+---+ +---+ +---+
pvowels: | | -----> pvowels[0]: | | ------> pvowels[0][0]: | |
+---+ +---+ +---+
pvowels[1]: | | ----+ pvowels[0][1]: | |
+---+ | +---+
... | ...
+---+ | +---+
pvowels[r-1]: | | --+ | pvowels[0][c-1]: | |
+---+ | | +---+
| |
| | +---+
| +-> pvowels[1][0]: | |
| +---+
| pvowels[1][1]: | |
| +---+
| ...
| +---+
| pvowels[1][c-1]: | |
| +---+
...
由于pvowels
指向指向char
的指针序列中的第一个,因此其类型必须为"指针指向char
"或{{ 1}}。
当您为char **
类型的对象序列*alloc
内存时,目标指针必须具有类型T
:
T *
在上面的语句中,表达式 T *p = malloc( n * sizeof *p ); // allocating a sequence of T
的类型为*p
,因此T
等同于sizeof *p
。
如果我们用指针类型sizeof (T)
替换T
,它看起来像
P *
我们不是为P **p = malloc( n * sizeof *p ); // allocating a sequence of P *
序列分配空间,而是为一系列指针分配空间到P
。由于P
的类型为p
,因此表达式P **
的类型为*p
,因此P *
与sizeof *p
相同。
请注意,在上面sizeof (P *)
的情况下,你所拥有的是而不是一个pvowels
的二维数组; char
指向一系列指针中的第一个,每个指针指向pvowels
序列中的第一个。您可以将它索引为它是一个2D数组,但它的结构不像2D数组; "行"在记忆中并不相邻。
答案 2 :(得分:0)
malloc
返回一个void *
指针,在较新版本的C中可以指定给任何指针类型,而无需显式转换。情况并非总是如此,并且是经常留下的历史文物。
答案 3 :(得分:0)
pvowels的类型为char **(指向字符指针(char *)的指针)。因此malloc(返回void *)必须被转换为"类型" pvowels。
如果你删除了sizeof函数中的char *,那么它的工作方式就是你想要的(malloc保留的内存为char类型的大小),它可能会保存char指针的地址。
如果你删除了cast char **,我的编译器会抛出一个错误,正如我所期望的那样(在cpp中编译)。在c中,它工作正常。