C language notes: linked list node implementation skills-the magical use of struct

    Linked list node implementation skills-the magical use of struct

The author's ability is limited. If you find any errors during the reading process, please also contact me to point out the errors and avoid the readers from learning the wrong knowledge later. Thank you!


nonsense

Although the C language only provides a very simple syntax, it does not affect the C language programmers to use C to achieve many amazing advanced functions.

This article introduces a technique for implementing linked list nodes that are very common in the C language.

Maybe you have read several books in C language, and have seen relevant introductions, but you do n’t care much, so here we will learn in detail.

Next, we will describe the implementation of a linked list node. Don't be disappointed. Its implementation may not be as simple as you think.

Node definition

typedef struct _LIST_ENTRY {
  struct _LIST_ENTRY *Next;
} LIST_ENTRY, *PLIST_ENTRY;

LIST_ENTRY represents a node of the doubly linked list. Next is a pointer to the next node.

But for the above nodes, we can't use it, because it can't contain any additional information except that it can represent a node.

Well, here we assume that we want to create a linked list representing students. Let ’s first define the student structure.

typedef struct _STUDENT {
  char name[64];
  int  age;
} STUDENT, *PSTUDENT;

We write a structure that represents the students at hand, it is very simple, because here we only use it to illustrate that if we use LIST_ENTRY, and do not want to explain how to build a student management system.

In order for the STUDENT structure to become a node of the linked list, we need to merge them. Then our STUDENT structure becomes like this:

typedef struct _STUDENT {
  LIST_ENTRY list_entry;
  char name[64];
  int  age;
} STUDENT, *PSTUDENT;

Note that we nested the LIST_ENTRY structure at the beginning of the STUDENT structure, which will make the subsequent implementation much simpler. It is certainly possible to put it in other positions, but it will complicate matters.

use

Now that the structure is defined, let's take a look at how we use this structure, and the cleverness of this structure, which is what this article wants to express.

Once again, this article is to describe the beauty of this structure usage, and is not intended to implement a complete linked list. So only the most rudimentary version is given.

#define GET_STUDENT(address, type, field) ((type *)( \
  (char *)(address) - \
  (char *)(&((type *)0)->field)))

PLIST_ENTRY list_header = NULL; // 链表头

// 在链表的尾部添加一个新的节点
int add_student(char* name, int age) {
  // create a student with the given parameters
  PSTUDENT student = malloc(sizeof(STUDENT));
  if (student == NULL)
    return -1;
  memset(student, 0, sizeof(STUDENT));

  strcpy(student->name, name);
  student->age = age;

  if (list_header == NULL) {
    list_header = &student->list_entry;
  } else {
    PLIST_ENTRY p = list_header;
    while (p->Next) {
      p = p->Next;
    }
    p->Next = &student->list_entry;
    // student->list_entry.Next is NULL
  }
}

int main() {

  // 添加两个节点
  add_student("student abc", 22);
  add_student("student ijk", 25);

  // 遍历整个链表
  请注意这里!!!!
  /////////////////////////////////////////////////////
  for (p = list_header; p != NULL; p = p->Next) {
    // get the student struct
    PSTUDENT student = GET_STUDENT(p, STUDENT, list_entry);
    // PSTUDENT student = (PSTUDENT)(((char*)p - (char*)(&((PSTUDENT)0)->list_entry)));
    printf("student name: %s, student age: %d\n", student->name, student->age);
  }
  /////////////////////////////////////////////////////

  // 省略释放内存的代码
  return 0;
}

Parsing

If you have seen its ingenuity so far, you do n’t need to waste time to watch the next part.

What is the point of the above code? The
point is the macro GET_STUDENT .

In order to facilitate debugging, we provide a 42-line macro expansion form to facilitate debugging.

  1. The first thing to note is that the type of each node in our linked list is STUDENT, not LIST_ENTRY.
  2. However, it should be noted that the first field in our STUDENT structure is LIST_ENTRY, which is the prerequisite for our GET_STUDETN to work properly.
  3. So why does this work?
    First, add a breakpoint on line 42 to debug, and we get the following results:

Insert picture description here

Please note that the address of p at this time is the same as the address of student. This is because our LIST_ENTRY is placed in the first position of the STUDENT structure, and when we add a new node to the list, we add the STUDETN structure. In this case, we can assign a pointer of STUDENT structure to a pointer of LIST_ENTRY.

Here we are looking at the internal layout of the same student structure:
Insert picture description here

Here we first see that the memory address of our student is 0x00000000600049fb0, this value is the same as that shown in the above picture, because my computer is a 64-bit machine, so the address occupies 8 bytes.
Here we parse these bytes in detail meaning:
(1) because the first field of our student is LIST_ENTRY, and LIST_ENTRY next contains only a pointer to the next node in the list and therefore there is no doubt the former eight characters. 10a00400 06000000represent the first address of the next node of the current byte . Note that this is a small segment, so it is exactly the opposite of the address byte sequence seen in the first picture, with the most significant bit at the end.
(2) The parsed byte value is the third-to-last byte memory Used to save student-> name
(3) The last two bytes represent student-> age, and its value is small segment code 16.
4. Next, we let the loop continue, locate the second node of the linked list, take a look at the memory layout:

Insert picture description here

Verify what we said above, right. The second node address 0x0000000060004a010, which coincided with the 10a00400 06000000agreement, because we know list_entry- first node> is the current node Next pointing Its first two bytes 0 , Because the Next of the current node is empty. The next field is the same as the summary of (2) (3), we will not explain it here.

In summary, the structure of LIST_ENTRY and STUDENT cleverly uses the characteristics of the memory layout of the C language structure, and puts the STUDENT structure into a linked list of LIST_ENTRY. What are its advantages?

This allows us to put any structure into the linked list we defined using LIST_ENTRY, and we do n’t have to define related fields for each structure that needs to be put into the linked list separately so that they can be interconnected. After doing so, we are equal to the logic related to the linked list Extracted from the structure used to actually store information, we can hardly pay attention to the real student type stored in the linked list when writing the method of operating the linked list.

Any ideas are welcome.

End…

Published 27 original articles · praised 31 · 40,000+ views

Guess you like

Origin blog.csdn.net/zhaoruixiang1111/article/details/103284681