C99指向包含另一结构体指针的结构体的指针

3

《C程序设计语言》第二版K&R在第131页上这样说,给定一组变量:

struct rect r, *rp = &r;

地点:

struct rect {
    struct point pt1;
    struct point pt2;
};

并且

struct point {
    int x;
    int y;
}

那么这四个表达式是等价的:
r.p1.x
rp->pt1.x
(r.pt1).x
(rp->pt1).x

在同一页的早些位置,我们看到这个内容:
p->member_of_structure

这里“refers to the particular member”描述了特定成员。

我将连字符改为下划线,以确保我们不会与减号混淆。

非常好,我可以看到我们有一个嵌套的结构体,因为结构体rect包含一个结构体point。

如果定义rect时pt1和pt2都是指向结构体point的指针怎么办?

这就是我在以下代码片段中遇到问题的地方:

typedef struct some_node {
    struct timespec *tv;
    struct some_node *next;
} some_node_t;

我将在这里创建一个链表,这并不是问题。

真正的问题是:

struct timespec some_tv;
clock_gettime( CLOCK_REALTIME, &some_tv )
/* set up the head node */
struct some_node *node_head =
                ( struct some_node * ) malloc( sizeof( some_node_t ) );
node_head->tv = calloc ( (size_t) 1, sizeof ( struct timespec ) );

这一切都很好,我很好地获得了我的node_head。 我甚至可以轻松获取嵌套的struct timespec node_head->tv。

真正的问题是如何将内部的tv.sec设置为some_tv.sec的值,就像这样:

((struct timespec *)(*node_head.tv)).tv_sec = some_tv.tv_sec;

我遇到了一个错误:
line 22: error: left operand of "." must be struct/union object

我正在查看K&R,发现书中的示例没有指向结构体内部的指针。

我只能通过试错来得到我想要的结果,但这很令人沮丧。我可以创建一个临时变量"struct timespec temp",然后将temp = &node_head.tv...但是不行...我认为那样更糟糕。

我在这里漏掉了什么?

当然,答案很简单,只需使用foo->bar->here语法即可。

修改代码以删除malloc上的转换,并使用正确的语法:

/* set up the node list */
struct some_node *node_head =
                calloc( (size_t) 1, sizeof( some_node_t ) );

node_head->tv = calloc ( (size_t) 1, sizeof ( struct timespec ) );
node_head->tv->tv_sec = some_tv.tv_sec;
node_head->tv->tv_nsec = some_tv.tv_nsec;
node_head->next = NULL;

调试器确认了这一点:
(dbx) print *node_head
*node_head = {
    tv      = 0x1001453e0
    next    = (nil)
}

(dbx) print *node_head.tv
*node_head->tv = {
    tv_sec  = 1363127096
    tv_nsec = 996499096
}

很好用。明显我需要咖啡。 :-)


旁注:在C语言中,您不应该强制转换malloc的结果... - Oliver Charlesworth
抱歉,我有点手忙脚乱。通常我使用calloc而不是malloc。 - paul lanken
考虑到您最初使用动态分配节点结构,为什么不直接使用“struct timespec tv;”成员而不是指针,并且必须调用YAMI(即另一个malloc调用)?或者您是在秘密测试堆收集器的碎片整合算法吗? - WhozCraig
我在撰写这个问题时非常小心,正如你所看到的那样,但解决方案却是如此微不足道,让我感到惊讶。我想我应该将malloc改为calloc,因为我经常使用它。这让我真的开始质疑自己了...非常感谢你用node_head->tv->tv_sec轻轻地敲了我一下。 - paul lanken
对WhozCraig:实际上,我在结构体中有一堆其他成员,但为简单起见,我将它们剥离了。你完全正确,我应该将 timespec 结构体留作一个好老的简单变量。然而...现在我完成了这个..我需要一个函数来添加列表中的下一个节点,并插入数据等等。所以...列表中可能有成千上万个节点,内存管理器能够正常工作...没有问题。 - paul lanken
5个回答

5
这已经足够了吗?
node_head->tv->tv_sec

2
其他回答已经给出了,但是如果不清楚的话,“->”就相当于你自己使用“*”进行“解引用”。
因此,
 rp->pt1
 (*rp).pt1

是等价的。

这意味着你有一个经验法则,即“处理指针时使用->”。

因此,在你所提到的情况下,

node_head->tv->tv_sec

将执行您所需的操作,并等效于

(*node_head).tv->tv_sec
(*(*node_head).tv).tv_sec

0
你应该将 (*node_head.tv) 改为 (*node_head).tv(或者像 Oli 写的那样使用 node_head->tv),因为 . 的优先级高于 *

0
一个初始的备注:您对tv的(struct timespec *)转换是不必要的,因为这已经是tv声明的方式了。
正如其他人所指出的那样,您真正需要使用ptr->field表示法:node_head->tv->tv_sec。箭头符号是一种语法糖,可以避免所有需要的(* rp)。 (* pt1)。x笨拙。
您问,
那么如果rect的定义是pt1和pt2都是指向结构点的指针,那么怎么办?
在这种情况下,@teppic的解决方案是您想要的,但要牢记的一项细节是数据结构的实际内存分配位置:在您的示例中,实际内存分配在rect中,在@teppic中,则为point。C语言的新手有时会因此感到困惑,特别是在长时间运行的程序中多次分配和释放内存(必须避免内存泄漏)。

0

从你的开头例子,如果你有:

struct rect {
    struct point *pt1;
    struct point *pt2;
};

struct point {
    int x;
    int y;
};

而且你有

struct rect *rp;

然后要访问x,只需使用:

rp->pt1->x

编译器可以弄清楚正在发生的事情。只有在使用空指针等情况下才需要进行转换。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接