3

Can someone explain 2 last lines of TAILQ_INSERT_TAIL macro:

#define TAILQ_INSERT_TAIL(head, elm, field) do {     
    (elm)->field.tqe_next = NULL;               /
    (elm)->field.tqe_prev = (head)->tqh_last;   /
    *(head)->tqh_last = (elm);                  /    
    (head)->tqh_last = &(elm)->field.tqe_next;  /   
} while (/*CONSTCOND*/0)

I understand, that, in generally, for insert in the tail of queue some node we need to do 4 things: 1) In node null pointer to next node. (first line of macro) 2) In last element of queue add pointer to node (second line of macro) 3) In node link pointer to previous node to last element in queue. 4) Update in queue head structure pointer to last element from last element in queue to our node.

1 and 2 points i understand clearly in macro, but 3 and 4 takes some misunderstanding, especially, i can not understand code like:

*(head)->tqh_last = (elm);

Call up last node in queue and overwrite it by current node? What will be with last node?

pradiptart
  • 970
  • 2
  • 9
  • 18
Andriy Veres
  • 81
  • 1
  • 1
  • 5

3 Answers3

1

(head)->tqh_last is a pointer to another pointer, see here, the last line:

(head)->tqh_last = &(elm)->field.tqe_next;

so, if we exchange the last two lines, you can understand it

(head)->tqh_last = &(elm)->field.tqe_next;
*(head)->tqh_last = (elm);

Edit

refer <sys/queue.h>

/*
 * Tail queue definitions.
 */
#define _TAILQ_HEAD(name, type, qual)                                   \
struct name {                                                           \
    qual type *tqh_first;           /* first element */                 \
    qual type *qual *tqh_last;      /* addr of last next element */     \
}
#define TAILQ_HEAD(name, type)  _TAILQ_HEAD(name, struct type,)

#define TAILQ_HEAD_INITIALIZER(head)                                    \
        { NULL, &(head).tqh_first }
  1. see qual type *qual *tqh_last; it has two stars
  2. see #define TAILQ_HEAD_INITIALIZER(head) { NULL, &(head).tqh_first }, it set thq_last to tqh_first's address, which is a pointer, so tqh_last is a pointer to pointer

Reference

See the similar question by Linus's answer(it's alot, just search At the opposite end of the spectrum in the page)

superK
  • 3,932
  • 6
  • 30
  • 54
  • '(head)->tqh_last' is pointer to last node in queue, ok? so '*(head)->tqh_last' get the data, that stored in '(head)->tqh_last' ? – Andriy Veres Jan 28 '18 at 15:59
  • @WhiteBear It is not just a pointer to the last node, but to last node's member(a pointer to a pointer). See the definition of `TAILQ_HEAD` in `` – superK Jan 29 '18 at 01:33
1

tqe_prev and tqh_last are both pointer of pointer, so

(elm)->field.tqe_prev and (head)->tqh_last 

both have the address value of the intermediate pointer.

*(head)->tqh_last

actually, hold the address value of elm. Now it is easy to understand, the 2nd line update the inter-mediate pointer of (elm)->field.tqe_prev, now elm can connect to previous last elm. The 3rd line update the value of intermediate point of (head)->tqh_last, now last elm is point to elm. The 4th line update the value of (head)->tqh_last with the address of (elm)->field.tqe_next, remember this is not NULL as 1st line, it use its address.

JJ Asghar
  • 614
  • 5
  • 15
Xq Liao
  • 11
  • 2
0

Maybe it's too late to answer but I was searching the same question solution and discovered below while going through the code.

What they are doing by below code is

*(head)->tqh_last = (elm); 

This is a bit tricky to understand, what actually this piece of code is doing is to hold the new node address, but as this is a double pointer and this location is actually being hold by tqh_first double pointer. ( Check the TAILQ_INIT )

So, for the first insert call, it is going to update the thq_first of the head data with the new node. If you notice, actually TAILQ_INIT is doing the trick to assign the last pointer to the address of the first pointer.

   #define  TAILQ_INIT(head) do {                   \
        (head)->tqh_first = NULL;                   \
        (head)->tqh_last = &(head)->tqh_first;      \ <- This line
   } while (0);

But just after this in TAILQ_INSERT_TAIL we are updating the same address with the end pointer of the node it self.

(head)->tqh_last = &(elm)->field.tqe_next;

This is because for the next iteration we want to link the tqe_next with the new node.

if you look the way TAILQ_FOR_EACH then you will understand the above logic why they made like this. The first element is pointed by the HEAD data structure but the next elements are pointed by the Nodes itself.

#define TAILQ_FOREACH(var, head, field)                 \
    for (var = TAILQ_FIRST(head); var; var = TAILQ_NEXT(var, field))

Note - This head->tqh_last is changed in every API and based on that it will take effect. above is just an example of how the pointers are used in case first time we use INIT then Insert at TAIL.

pradiptart
  • 970
  • 2
  • 9
  • 18