Macro containing_record is very useful and convenient. Its main functions are as follows:
Calculate the pointer of a struct Based on the pointer of a member!
Let's start with a simple example:
We define a struct with the following types:
typedef struct{ int a; int b; int c;}ss;
This is a very simple struct. There is nothing special about it. Perform a slight analysis on this struct:
Struct size (in bytes): 4 + 4 + 4 = 12 bytes
Offset of member A: 0
Offset of member B: 4
Offset of member C: 8
We use ss to define a variable:
Ss s = {1, 2, 3 };
Then the values of A, B, and C are respectively: a = 1, B = 2, c = 3.
In fact, the compiler assigns values to member variables when generating code:
If the address of S is: 0x12000000, then:
* (Int *) (char *) & S + 0) = 1;
* (Int *) (char *) & S + 4) = 2;
* (Int *) (char *) & S + 8) = 3;
That is to say, the variable offset is added on the basis of the & S address to determine the member pointer and assign a value. Therefore:
& S-> A will get 0x12000000 + 0 = 0x12000000
& S-> B will get 0x12000000 + 4 = 0x12000004
& S-> C will get 0x12000000 + 8 = 0x12000008
So: structure address + member variable offset = member variable address
Move the following item: Address of the member variable-offset of the member variable = address of the Structure
Wow, this is the address we want, not a subtraction ~~~ Bytes
First, we know the address of the member variable.
Second, we need to get the offset of the member variable (assuming the offset of member B ).
What should we do? We can do this:
& S-> B-(unsigned long) & S, so that the offset of member B can be obtained. However, & S is what we need, and it is obviously unknown for the time being, in this case...
So, let's perform subtraction again (incorrect C language expression, but the result is okay. Here it just seems clear ):
& (S-S)-> B-(unsinged long) & (s-s ),
To ensure consistency of types, you must:
S-s = (SS *) 0
(Unsigned long) & (S-S) = (unsigned long) (SS *) 0 = 0. simply omit this part.
Then, the simplification result is: & (SS *) 0)-> B-(unsigned long) 0
The simplest result is: & (SS *) 0)-> B, which is the offset of B.
Haha, it's easy. It's just a wonderful use of the 0 pointer. It's only two subtraction operations in total ~ It is certainly not a problem for your math emperor ~
Among them, we need to know the prototype of the SS struct, which is a member variable B in the SS struct (in fact, both are the same, but must be consistent with the variable that provides the pointer)
To sum up, we need to provide the address of a member variable in the struct, the prototype of the struct, and a member variable in the struct (which is the same as the previous variable)
The final containing_record is defined:
# Define containing_record (ADDR, type, field) (type *) (unsigned char *) ADDR-(unsigned long) & (type *) 0)-> Field ))
ADDR: the address of a member variable in the struct.
Type: struct prototype
Field: a member of the struct (same as the previous one)
All the conclusions come out. This is the 10 thousand formula. No matter which result of the member variable is correct, this is relative to knowing the address of the first variable:
If you know the address of the first Member (Pa = & S-> A), this is the simplest case:
Directly force type conversion: (SS *) Pa. At this time, & (type *) 0)-> field is exactly 0.
Therefore, the result is (type *) ADDR. The simplest case is also the one we are most likely to think of, for example, placing the linked list element at the beginning of the struct ~~~
Now the containing_record macro is finished ~
Now, when using two-way linked lists such as list_entry, you can use the containing_record macro to obtain the address of the entire struct In the traversal Link Table no matter where the linked list is placed ~
Remember to free the entire struct address when removing an element from the linked list. The operation function provided by wdk only disconnects the linked list element from the entire linked list ~~~
BTW:
The reason for converting ADDR to unsigned char * is that the unit of calculation in Pointer calculation is 1, that is, (unsigned char *) ADDR + 1 = ADDR + 1, if it is not converted, it is definitely incorrect.
Convert & (type *) 0)-> field to (unsigned long) Four byte widths, and ensure that the expression is not composed of two pointer arithmetic operations, because the C language standard does not define such an operation
I have written so much, so I hope I can't do anything ~
Girl don't cry (QQ: 191035066) @ 18:56:57 http://www.cnblogs.com/nbsofer