有人可以在使用malloc时解释(char *)和(char **)的用法或用途

时间:2017-05-01 19:00:48

标签: c pointers casting malloc

我按照以下教程进行操作: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));

本教程没有解释这是什么,我假设它是某种类型的类型转换,也许对于这个例子来说实际输入它是不重要的,而作者认为显式键入强制转换非常重要。

另外,我不熟悉双重' *'是(**)。

只是想确定我没有遗漏任何东西,谢谢你的任何解释。

4 个答案:

答案 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?有几个原因:

  • 作者在20世纪70年代末/ 80年代初开始编写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中,它工作正常。