1. Question proposal
We know that if a struct is defined as follows:
- struct _st {
- int a;
- char b;
- } st ;
We can access a or B through st. The method is st. a or if the st pointer pst exists, use pst-> ). However, if you know the element pointer in the struct, can you get the pointer of the current struct? Or, if I can only access B, can I access st and?
2. Practical Significance of this problem
First, what is the purpose of this operation? In fact, I have long known that linus in the Linux kernel uses the container_of () Macro, which is used in this method. Recently, I encountered a problem in the project and thought about a particularly good method. However, using this method can better solve the problem.
In general, the problem is abstracted: There are some blocks that need to be concatenated with multiple linked lists. For example, if there are 5 blocks in B1, B2, B3, B4, and B5, We Need To concatenate them with three linked lists with different functions:
Linked List 1: B1-> B4-> B5
Linked List 2: B2-> B5
Linked List 3: B1-> B2-> B3-> B4-> B5
The project wants to use the linked list library in glib for implementation. Here is an example of a one-way linked list in the linked list library in glib. See the Glib documentation for the Organization ):
- typedef struct {
- gpointer data;
- GSList *next;
- } GSList;
Data is linked by a data Pointer to implement a one-way linked list. Here, if someone has a recommended library, I 'd like to talk about it. Here, if we use the glib library, it is obviously not easy to implement the functions we want, because we do not know whether B2's next is to point to B5, such as linked list 2) or B3, such as linked list 3 ). We obviously need multiple next pointers to achieve this. If we use the following method:
- struct _block {
- int data;
- GSlist list1;
- GSlist list2;
- GSlist list3;
- };
Because the next pointer of each GSlist is directed to the list1 or list2/3 element of the next block, it is difficult to obtain the data of the data Field. At this time, you need to find data through the list1 pointer. This is the question we raised.
3. Consider
You can find out how the struct exists in the memory. For example:
In this way, we can use addition and subtraction to calculate the address of the struct of the integer a pointed to by pa. The compiler certainly knows all of the above, but how can we reflect it in code?
In fact, we don't need to know the problem above to express it in code. You can see the following method section.
4. container_of () Analysis
Since we know that in the Linux kernel, linus uses a tricky macro such as container_of () to obtain the pointer of the current struct through the pointer of elements in a struct. Here, you can use it directly.
The implementation method of container_of () is very simple and clever. Its core is two macro definitions:
- #ifndef offsetof
- #define offsetof(type, field) ((long) &((type *)0)->field)
- #endif /* offsetof */
-
- #ifndef container_of
- #define container_of(ptr, type, member) ({ \
- const typeof( ((type *)0)->member ) *__mptr = (ptr); \
- (type *)( (char *)__mptr - offsetof(type,member) );})
- #endif
There are three major macros: typeof (), offsetof (), and container_of (). Let me talk about some of my understandings:
- int main(void)
- {
- printf("%d.\n", (1,2,3));
- printf("%d.\n", ({int i=1,j;j=i+2;}));
- return 0;
- }
5. container_of () Example
Here is a simple example of using container_of:
- /*
- * desc : a simple example to use container_of() macro
- * mail : xzpeter@gmail.com
- */
- #include <stdio.h>
-
- #ifndef offsetof
- #define offsetof(type, field) ((long) &((type *)0)->field)
- #endif /* offsetof */
-
- #ifndef container_of
- #define container_of(ptr, type, member) ({ \
- const typeof( ((type *)0)->member ) *__mptr = (ptr); \
- (type *)( (char *)__mptr - offsetof(type,member) );})
- #endif
-
- typedef struct _A {
- int a;
- char b;
- long long c;
- double d;
- } SA ;
-
- SA sa = {
- .a = 10,
- .b = 'c',
- .c = 204,
- .d = 3.14,
- };
-
- int main(int argc,char *argv[])
- {
- double *pd = &sa.d;
- /* now we have a pointer pd -> sa.d, let's access element c with pd */
- printf("SA.c = %lld\n", container_of(pd, SA, d)->c);
- return 0;
- }
Here, I have defined a structure sa and obtained the sa. c value through the sa. d pointer. The output is:
- SA.c = 204
In this way, we can also get block. data through block. next.
References:
This article is from the LoudMouth Peter blog, please be sure to keep this source http://xzpeter.blog.51cto.com/783279/339497