【问题标题】:C programming. Pointers to structC 编程。指向结构的指针
【发布时间】:2017-04-02 18:00:41
【问题描述】:

这是我的第一篇文章。我对 C 的指针及其与结构的关系感到非常困惑。我已经搜索了更多信息,但无法真正得出结论。例如给定这个结构定义

typedef struct node
{
     int info;
     struct node *next;
}NODE;

那么这四种声明的区别和作用是什么;

1. NODE *node1 = malloc (sizeof(NODE));
2. NODE *node1 = (struct node *) malloc (sizeof(NODE));
3. NODE *node1 = (struct node *) malloc (sizeof(NODE *));
4. NODE *node1 = malloc (sizeof(NODE *));

提前致谢。

【问题讨论】:

  • 您的问题不在于指针或结构,而在于C++sizeof
  • 在 C 中,您不会从 malloc 转换返回值 - 请参阅 stackoverflow.com/questions/605845/…
  • @EOF - 谁提到了 C++?
  • 如果删除不必要的强制转换,则只剩下两个示例:struct 的内存和struct 指针的内存。
  • @JustinTime 指针总是相同大小的例外情况,这里有一个SO question 有更多链接。

标签: c list pointers stack structure


【解决方案1】:

它们是等效的,并且分配一块与 NODE 结构相同大小的内存。

1. NODE *node1 = malloc (sizeof(NODE));
2. NODE *node1 = (struct node *) malloc (sizeof(NODE));

这些都是错误的

3. NODE *node1 = (struct node *) malloc (sizeof(NODE *));
4. NODE *node1 = malloc (sizeof(NODE *));

第 3 行分配一个内存块,大小相当于一个指向 NODE 结构的指针。演员表允许您分配它而不会引发任何错误,并且可能没有警告。但这不是你想要的。

第 4 行与第 3 行相同。malloc 返回一个不需要强制转换的void *,但一个好的静态分析器应该会给你一个警告。

无论哪种方式,第 3 行和第 4 行都是缓冲区溢出和未定义行为的秘诀。

如果这样写,第 3 行和第 4 行将是正确的:

NODE **ptr = malloc(sizeof(NODE *));

【讨论】:

  • 附带说明一下,实现这种结构的一种更简洁的方法是改用sizeof(*node1)。对于不熟悉 C 的人来说,这似乎是错误的,但 sizeof 不是一个函数,实际上没有任何东西被取消引用。但是,使用此表示法可确保即使您稍后更改 node1 的类型,您的 sizeof 仍将适用于任何类型。
  • 同意,这就是我个人所做的。但我试图尽可能接近 OP 的原始代码
  • 在 C 中转换 malloc 的返回值可以隐藏错误(最突出的是,忘记 #include <stdlib.h>)并且被认为是大写 W 错误。跨度>
  • 他们不是“完全错了”。您根本无法通过派生指针类型的类型的表达式来访问分配的对象,但是OP的代码根本不访问分配的对象,因此代码很好(如果没用的话)。
  • 感谢您的回答。目前无法投票给你。
【解决方案2】:
// node1 is a pointer to (heap) memory block of size NODE
1. NODE *node1 = malloc (sizeof(NODE));    

// same as 1. (but 1. is the preferred way)
2. NODE *node1 = (struct node *) malloc (sizeof(NODE));

// (Wrong) node1 is a pointer to (heap) memory block of size pointer to NODE (a pointer to _usually_ 4 bytes)
//         but you cast it to pointer to NODE (a pointer to more than 4 bytes)
3. NODE *node1 = (struct node *) malloc (sizeof(NODE *));

// (Wrong) same as 3
4. NODE *node1 = malloc (sizeof(NODE *)); 

坚持使用数字 1。因为这是在堆中分配对象的常用方法(动态分配):Object *object = malloc(sizeof(Object))。您可以忘记 2. 3. 4. 这会使问题复杂化,而实际上并不是您所需要的。

【讨论】:

  • 谢谢——这样更好
  • 好的。谢谢。我可能需要一些时间来吸收侧面的 cmets。
  • @JamesReal:主要意思是示例 3 和 4 没有分配足够的空间让您能够将空间用于预期目的。
  • @JamesReal 已更新,坚持使用数字 1,这是简单而正确的开始方式。
  • @JonathanLeffler 是的,这是解释为什么 3 和 4 错误的简洁方式。
【解决方案3】:

给定:

  1. 节点 *node1 = malloc (sizeof(NODE));
  2. NODE *node1 = (struct node *) malloc (sizeof(NODE));
  3. NODE *node1 = (struct node *) malloc (sizeof(NODE *));
  4. NODE *node1 = malloc (sizeof(NODE *));

一切都很糟糕

3 和 4 真的很糟糕

3) 和 4) 没有为结构分配足够的内存,但只有足够的内存来保存指向结构的指针(比如 32 位系统上的 4 个字节),然后告诉编译器将该内存视为足以占用NODE

1) 是为结构分配足够内存的正确方法(注意下面描述的其他问题)

2) 是不好的做法,因为您真正的意思是:

  NODE *node1 = (NODE *) malloc(sizeof(NODE));

即使NODEtypedef struct node (2) 表示你肯定知道这一点。

话虽如此,

所有这些都是不好的做法,原因将在下文解释。

在实践中你真正想做的是这样的:

只有当你想让内部结构不透明时才使用 typedefs(stdio 执行 FILE 的方式),在声明中使用 struct foo bar; 比使用结构体 typedef 更好。特别是当结构暴露于程序的其他部分时。

typedef 更适合用于确保类型在不同架构上的统一解释,例如typedef unsigned long long uint64_ttypedef unsigned uint64_tstdint.h 中的 32 位和 64 位英特尔 x86 cpu 上的使用

一个很好的例子

struct node {
     struct node *next;
     size_t n_dlen;
     void * n_data;
};


int main() {
    struct node *node;

    node = malloc(sizeof(*node)); 
}

请注意,我使用 sizeof(*node) 而不是 sizeof(struct node),因为如果我稍后决定该节点应该是其他类型,比如 struct new_node *,那么我所要做的就是更改声明和 @ 987654333@ 还是可以的。

【讨论】:

  • 再次读取 OP 的 typedef。
  • @EOF 我想我就是这么做的。
  • 与您的断言相反,NODE 在 OP 的代码中是 typedefd 到 struct node,而不是 struct node*(这确实是个坏主意)。
  • 看起来有十几种方法可以做到这一点。该死的,我希望我能和你们一样好。
  • @AhmedMasud 您确实意识到您的答案的编辑历史对所有人都是可见的?声称答案没有说它确实做了什么不是一个好主意。
猜你喜欢
  • 2021-07-14
  • 1970-01-01
  • 2017-01-04
  • 2021-01-12
  • 2011-11-30
  • 2016-08-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多