3

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. :-)

paul lanken
  • 197
  • 1
  • 14
  • Tangential note: [you shouldn't cast the result of `malloc` in C...](http://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc) – Oliver Charlesworth Mar 12 '13 at 22:12
  • Sorry, I was flailinga bit. I usually use calloc and not malloc. – paul lanken Mar 12 '13 at 22:14
  • Considering you dynamic-allocating the node struct to begin with, what earthly reason is there for not just using a `struct timespec tv;` member rather than a pointer and having to invoke YAMI (yet-another-malloc-invoke)? Or are you secretly testing the heap collector's fragmentation consolidation algorithm? – WhozCraig Mar 12 '13 at 22:14
  • Well I took great care in writing this question, as you can tell, and I am just amazed at how trivial the solution was. I think I shall revise that malloc to a calloc as I use that more often. This had me really questioning myself .. thank you ever so much for giving me a slight smack upside the head with node_head->tv->tv_sec. – paul lanken Mar 12 '13 at 22:17
  • To WhozCraig : in fact, I had a stack of other members in the struct but I ripped them out for simplicity. You are totally correct and I should just leave the timespec struct as a good ol simple variable. However ... now that I have this done .. I need a function to add the next node on the list, and insert data etc etc. So .. there may be thousands in the list and the memory manager works .. no problem there. – paul lanken Mar 12 '13 at 22:21

5 Answers5

5

Isn't this sufficient?

node_head->tv->tv_sec
Oliver Charlesworth
  • 267,707
  • 33
  • 569
  • 680
2

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
andrew cooke
  • 45,717
  • 10
  • 93
  • 143
0

You should change (*node_head.tv) to (*node_head).tv (or node_head->tv as Oli wrote) as . has higher precedence over *.

MByD
  • 135,866
  • 28
  • 264
  • 277
0

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.

teppic
  • 8,039
  • 2
  • 24
  • 37
0

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).

JohnJ
  • 4,753
  • 2
  • 28
  • 40