Reputation: 197
The K&R "The C Programming Language" 2nd edition says on page 131 given a set of variables thus :
struct rect r, *rp = &r;
where :
struct rect {
struct point pt1;
struct point pt2;
};
and
struct point {
int x;
int y;
}
then these four expressions are equivalent :
r.p1.x
rp->pt1.x
(r.pt1).x
(rp->pt1).x
Earlier on that same page we see this :
p->member_of_structure
Which is described as "refers to the particular member".
I changed the hyphens to underscores to ensure we could not be confused with a minus sign.
So great, I can see we have what I would refer to as a nested struct because the struct rect contains within it a struct point.
Well what is the definition of rect were such that pt1 and pt2 were both pointers to a struct point?
Here is where I hit my troubles with the following code bits :
typedef struct some_node {
struct timespec *tv;
struct some_node *next;
} some_node_t;
Clearly I will be making a linked list here and that is no problem.
What is a really big problem is this :
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 ) );
That all works great and I get my node_head just fine. I even get my nested struct timespec node_head->tv just fine.
What is a real problem is trying to figure out how to set that inner tv.sec to the value in some_tv.sec like so :
((struct timespec *)(*node_head.tv)).tv_sec = some_tv.tv_sec;
I get an error :
line 22: error: left operand of "." must be struct/union object
So I am looking in the K&R and I see that the example in the book does not have a pointer to a struct within the struct rect.
I have resorted to trial and error to get what I want but this is maddening. I could create a temporary variable of type "struct timespec temp" and then set temp = &node_head.tv ... but no ... that won't work. That would be worse I think.
The answer was trivial, of course, simply use foo->bar->here syntax.
Modify the code to drop the cast on the malloc and use the correct syntax :
/* 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;
The debugger confirms this :
(dbx) print *node_head
*node_head = {
tv = 0x1001453e0
next = (nil)
}
(dbx) print *node_head.tv
*node_head->tv = {
tv_sec = 1363127096
tv_nsec = 996499096
}
Works great. clearly, I need coffee. :-)
Upvotes: 3
Views: 1591
Reputation: 8195
From your opening example, if you have instead:
struct rect {
struct point *pt1;
struct point *pt2;
};
struct point {
int x;
int y;
};
and you have
struct rect *rp;
Then to access x
you just use:
rp->pt1->x
The compiler can figure out what's going on. You only need casting if you have void pointers and such.
Upvotes: 0
Reputation: 4833
An initial remark: your (struct timespec *)
cast of tv
is not necessary, since that is already how tv
is declared.
As others have indicated, you really only need to use the ptr->field
notation here: node_head->tv->tv_sec
. The ->
notation is a bit of syntactic sugar to avoid all the (*rp).(*pt1).x
clumsiness which would otherwise be required.
You asked,
Well what is the definition of rect were such that pt1 and pt2 were both pointers to a struct point?
in that case @teppic's solution is what you want, but one subtlety to keep in mind is where the actual memory for the data structure is being allocated: in your example, the actual memory allocation is in rect
, and in @teppic's, it's point
. This point, sometimes tricky for newcomers to C, matters, particularly when you allocate and free the memory many times in long-running programs (necessary to avoid memory leaks).
Upvotes: 0
Reputation: 46872
the other answers have it, but in case it's not clear, ->
is the same as doing a "dereference" with a *
yourself.
so
rp->pt1
(*rp).pt1
are equivalent.
which means that you have the rule of thumb "use ->
when dealing with pointers".
so in the case you had,
node_head->tv->tv_sec
will do what you want, and is equivalent to
(*node_head).tv->tv_sec
(*(*node_head).tv).tv_sec
Upvotes: 2
Reputation: 137382
You should change (*node_head.tv)
to (*node_head).tv
(or node_head->tv
as Oli wrote) as .
has higher precedence over *
.
Upvotes: 0