为什么这个指针声明适用于 malloc()?

Why does this pointer declaration work with malloc()?

提问人:programme3219873 提问时间:8/26/2022 更新时间:8/26/2022 访问量:108

问:

我是 C 的新手,对特定的指针声明有疑问:

这是我写的一个程序:

#include <stdlib.h>

struct n {

       int x;
       int y;
};


int main()
{
    struct n **p = malloc(sizeof(struct n));

    return 0;
}

这里的声明是不正确的,但为什么不呢?

这是我的思考过程:

  • 的手册页指定它返回一个指针:malloc
The malloc() and calloc() functions return a pointer to the
allocated memory, which is suitably aligned for any built-in
type.
  • 类型是指向另一个指针的指针。pstruct n**

  • 但是,这个声明在理论上不应该起作用,因为:

  1. malloc返回类型(指针)struct n*
  2. 并指向返回的指针pmalloc
  3. 因此,它本质上是指向另一个指针的指针
  4. 所以满足的类型p

对不起,如果这是一个愚蠢的问题,但我真的很困惑为什么这不起作用。提前感谢您的任何帮助。

C 指针 结构 malloc

评论

1赞 Fe2O3 8/26/2022
它将按编写的方式编译,但请尝试弄清楚如何添加更多代码以使用双 ptr。轮子会很快从推车上掉下来......

答:

6赞 Eric Postpischil 8/26/2022 #1

返回类型 不是 ,无论它如何调用。返回类型为 。mallocstruct n *mallocvoid *

初始化 type 值的对象会将其隐式转换为 。这种隐式转换是允许的,因为初始化规则遵循 C 2018 6.5.16.1 1 中的赋值规则,其中声明允许的赋值之一是:struct n **void *struct n **

...左操作数具有原子指针类型、限定指针或非限定指针类型,并且(考虑到左操作数在左值转换后将具有的类型)一个操作数是指向对象类型的指针,另一个操作数是指向 的限定或非限定版本的指针,并且左指向的类型具有右指向的类型的所有限定符;void

  1. 并指向返回的指针pmalloc

否,该值初始化为返回的值。然后指向分配的内存。pmallocpmalloc

此代码错误地为 (using ) 分配了足够的空间,但将该空间的地址分配给 即 。要指向 ,请使用 或,最好是 。struct nmalloc(sizeof(struct n))struct n **pstruct nstruct n *p = malloc(sizeof (struct n));struct n *p = malloc(sizeof *p);

要指向指向 a 的指针,首先创建一些指向 的指针,如上所述。那么指向该指针的指针将是 。struct nstruct nstruct n *p = malloc(sizeof *p);struct n **pp = &p;

如果要为这些指针到指针分配空间,可以使用 ,然后可以将指针填充到 with 。但是,您不应在没有充分理由的情况下添加此额外的分配层。struct n **pp = malloc(sizeof *pp);struct n*pp = malloc(sizeof **pp);

请注意,该表单通常更受欢迎,因为前者会自动使用指向的类型。后者容易出错,例如有人错误地输入了 的类型并且设置不正确,或者有人后来更改了 的声明,但省略了对调用的相应更改。MyPointer = malloc(sizeof *MyPointer);MyPointer = malloc(sizeof (SomeType));MyPointerMyPointerSomeTypeMyPointerSomeTypemalloc

评论

0赞 programme3219873 8/26/2022
这是一个很好的答案,非常感谢您花时间解释这一点,我非常感谢。我还有最后一个问题:在您提到的使用您的回答中,这究竟是如何工作的?我可以看到您取消了引用,但这不会只会导致未定义的行为,因为它处于分配过程中并且可能指向任何内容?我对这将如何工作感到非常困惑。malloc(sizeof(*MyPointer))MyPointer
1赞 Eric Postpischil 8/26/2022
@programme3219873:不计算表达式操作数 。编译器仅使用它来分析其类型。sizeof
1赞 William Fleetwood 8/26/2022 #2

这实际上不起作用。在此示例中,malloc 返回一个 void *,它指向堆中新分配的位置,该位置足够大,可以容纳结构体 n。

请注意,malloc() 返回类型 void *,它基本上是指向任何潜在类型的指针,malloc() 不返回类型 struct n *(指向您声明的结构类型的指针)。void * 具有能够强制转换为任何其他类型的指针并再次转换的特殊属性。

总而言之,这意味着您的代码实际上不起作用,因为结构 n** 的第一个取消引用将是结构 n,而不是结构 n*。如果尝试取消引用两次,则很可能会得到无效的内存引用并崩溃。

您的代码之所以能够编译和运行而不会崩溃,是因为:

  1. 编译器自动将 void * 强制转换为结构 n **
  2. 您永远不会尝试实际取消引用指针,这意味着您永远不会尝试无效的内存引用。

评论

1赞 chux - Reinstate Monica 8/26/2022
"void *具有能够强制转换为任何其他对象类型的指针的特殊属性:“ 到/从函数指针有问题。void *
0赞 William Fleetwood 8/26/2022
@chux-ReinstateMonica 我没有意识到这一点,老实说,我很少在 C 中使用函数指针,但我认为这是有道理的,因为 C 中的函数不是第一类值。究竟是什么样的问题?
0赞 chux - Reinstate Monica 8/26/2022
示例:函数指针可能是 128 位和 64 位。如果尝试转换回函数指针,则将此类函数指针保存到其中会丢失不可恢复的信息。C 缺乏一个真正通用的指针。void *void *
1赞 Fe2O3 8/26/2022 #3

简化问题和理解可能会到来:

int main()
{
    struct n data;
    struct n *pData = &data; // skipped in your version
    struct n **ppData = &pData;

    // struct n **ppData = &data; // Will not compile

    // struct n **ppData = (struct n **)&data; // Casting but wrong!

    return 0;
}

由于返回 void ptr,因此您可以自由地将指针存储到任何指针数据类型中。如果你把它放到错误的数据类型中......malloc()

评论

0赞 Eric Postpischil 8/26/2022
Re “因为返回一个 void ptr,所以你可以自由地将指针存储到任何指针数据类型中”:违反约束,将生成诊断。malloc()void (*f)() = malloc(1);
0赞 Fe2O3 8/26/2022
@EricPostpischil 你把我带到了那里。但是,该函数无效(如果可以设想的话)。它本身不是一个 void 数据类型。苹果和橙子,你不觉得吗?returns
0赞 Eric Postpischil 8/26/2022
@Fe2O3:结果未存储到函数中。该初始化要求将其存储到指针中。malloc