paul lanken
paul lanken

Reputation: 197

C99 pointer to a struct which contains a pointer to a struct

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.

What am I missing here ?

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

Answers (5)

teppic
teppic

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

JohnJ
JohnJ

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

andrew cooke
andrew cooke

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

MByD
MByD

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

Oliver Charlesworth
Oliver Charlesworth

Reputation: 272667

Isn't this sufficient?

node_head->tv->tv_sec

Upvotes: 5

Related Questions