C Language Structural offset
Example 1
Let's define the requirements first:
The known structure body types are defined as follows:
struct node_t{
char A;
int b;
int c;
};
And the structure is 1Byte aligned
Please:
Structure body struct The offset of member variable C in node_t.
Note: The offset here refers to the offset from the starting position of the structure body.
When I see this problem, I believe that the solutions that emerge in different human brains may vary, and here are some possible ways to solve these problems:
Method 1
If you are familiar with the library functions of C, then the first thing you think of is the Offsetof function (actually just a macro, let's call it that), we man 3 offsetof view the following function prototype:
#include <stddef.h>
size_t offsetof (type, member);
With this library function, we can do this with one line of code:
Offsetof (struct node_t, c);
Of course this is not the focus of this article, please read on.
Method 2
When we are unfamiliar with the library function of C language, do not worry at this time, we can still use our own method to solve the problem.
The most direct idea is: "The address of the struct member variable C" minus the "start address of the structure body"
Let's first define a struct variable node:
Then compute the offset of the member variable C:
(unsigned long) (& (NODE.C))-(unsigned long) (&node)
& (NODE.C) is the address of the struct member variable C and is forced to be converted to unsigned long;
The &node is the starting address of the structure body and is also forced into unsigned long;
Finally, we subtract the above two values and get the offset of the member variable C.
Method 3
According to Method 2, we can still get the offset of member variable C without the help of library function. But as programmers, we should be good at thinking, is it possible to make some improvements to the code above, so that our code can be more concise? Before making specific improvements, we should analyze what the problem is with Method 2.
Believe that I don't have to say more, careful you must have noticed, one of the main problems with Method 2 is that we've customized a struct variable node, and although we don't have any limitations on how to customize variables, we need to think about new solutions when we're faced with a problem where we don't allow custom variables.
Before exploring new solutions, let's explore a small problem with offsets:
Small problem
This is a simple geometric question, assuming that the shift from point A to point B in the coordinates, how to calculate b relative to a offset? The problem is very simple for us, and most people will blurt it out and get answers for b-a.
So is the answer completely accurate? The more rigorous you think is obviously not, because, when a is the coordinate origin is a=0, the above answer b-a directly simplified to B.
What does this little simple question have to do with us?
We combine method 2 with the idea and the little questions above, are not soon getting the following association:
(unsigned long) (& (NODE.C))-(unsigned long) (&node)
And
B-a
The idea of our little problem is that when a is the coordinate origin, the b-a is reduced to B, and then to our method 2, when node's memory address is 0 (&node==0), the above code can be simplified to:
(unsigned long) (& (NODE.C))
Because the node memory address is ==0,
node.c //struct body node member variable c
We can use another way to express the following:
((struct node_t *) 0)->c
The above code should be better understood, because we know the structure of the memory address number 0, so we can directly through the memory address to access the structure of the member variable, the corresponding code meaning is to get the memory address number 0 of the structure struct node_t member variable c.
At this point, our offset method eliminates the custom variable struct node_t node, which is directly one line of code to solve:
(unsigned long) (& (((struct node_t *) 0)->c))
The above code is a little more concise than Method 2.
Here we define the above code function as a macro that is used to compute the offset of a member variable in a struct (the following example uses the macro):
#define OFFSET_OF (Type, member) (unsigned long) (& ((type *) 0)->member))
Using the macros above, you can directly get the offset of member variable C in the struct node_t of the struct body as follows:
Offset_of (struct node_t, c)
Example 2
As with Example 1, we first define the requirements as follows:
The known structure body types are defined as follows:
struct node_t{
char A;
int b;
int c;
};
int *p_c, which points to the member variable C of struct node_t x
1Byte alignment of structural body
Please:
The value of the member variable B of the struct body x?
To get this problem, we first do a simple analysis, the meaning of the topic is based on a pointer to a struct member variable, how to find the structure of another member variable value.
Then there are several possible solutions:
Method 1
Since we know that the structure is 1Byte aligned, the simplest solution to this problem is:
* (int *) ((unsigned long) p_c-sizeof (int))
The above code is very simple, the address of member variable C minus sizeof (int) to get the address of member variable B, and then cast to int *, finally take the value finally get the value of member variable B;
Method 2
The code for Method 1, though simple, is not very scalable. We hope to get the pointer p_node to the structure directly through P_c, and then access any member variables of the structure through P_node.
Thus we get the idea of calculating the starting address p_node of the structure body:
"Member variable C's address P_c" minus "C offset in struct body"
By Example 1, we get the offset of member variable C in struct struct node_t as:
(unsigned long) & (((struct node_t *) 0)->c)
So we get the starting address pointer p_node of the struct body as:
(Struct node_t *) ((unsigned long) p_c-(unsigned Long) (& ((struct node_t *) 0)->c))
We can also directly use the OFFSET_OF macro defined in Example 1, the above code becomes:
(Struct node_t *) ((unsigned long) p_c-offset_of (struct node_t, c))
Finally, we can use the following code to get the value of the member variable a,b:
P_node->a
P_node->b
We also define the functionality of the above code as the following macro:
#define Struct_entry (PTR, type, member) (type *) (unsigned long) (PTR)-offset_of (type, member)
The function of the macro is to obtain a pointer to the struct body by using a pointer to any member variable of the struct.
We use the macros above to modify the previous code as follows:
Struct_entry (P_c, STRUCT node_t, C)
The P_c is a pointer to the struct NODE_T member variable C of the structure body;
struct node_t structural body type;
C is the member variable that the P_c points to;
Note:
Some instructions about address operations in the preceding example:
int a = ten;
int * P_a = &a;
Set p_a = = 0x95734104;
The following are the results of the compiler calculation:
- P_a + = = p_a + sizeof (int) *10 =0x95734104 + 4*10 = 0x95734144
- (unsigned long) p_a + = 0x95734104+10 = 0x95734114
- (char *) p_a + = = 0x95734104 + sizeof (char) *10 = 0x95734114
From the above three kinds of situations, I believe you should be able to understand what I want to express the meaning.
Structure member variable access method
accessing struct member variables? What can be thought about such a simple question? I'm wondering, and it's weird. In that case, keep reading with this strange question.
Example 3
Our discussion begins with a simple example:
The known structure body types are defined as follows:
struct node_t {
char A;
int b;
int c;
};
And the structure body is 1Byte aligned:
Next we explore several ways to access the struct member variable C:
Scenario 1
If the program defines a struct node_t type of variable node as follows:
Then we can access the member variable C directly in the following way:
Scenario 2
If a pointer to a struct node_t type is defined in the program P_node as follows:
struct node_t node;
struct node_t *p_node = &node;
Alternatively, the memory on the heap with a type of struct node_t is allocated as follows:
struct node_t *p_node= (struct node_t *) malloc (sizeof (struct node_t));
Then we can access the member variable C by using the following method:
Scenario 3
Both of these methods of access are more common, and are familiar with, below we discuss a kind of people are not particularly familiar with the situation is not very common:
If only one memory address numeric addr_node is given in the program, and the address addr_node the beginning of a memory, pointing to a piece of memory of type struct node_t, the Addr_node statement is as follows:
At this point, how do we access the member variable C based on this memory address?
Because we know the starting address addr_node of the struct, we force the type conversion, and we get a pointer to the structure body P_node:
struct node_t *p_node = (struct node_t *) Addr_node;
Then we can access the member variable C by the way of scenario 2;
Scenario 3 to convey means that we can access our struct member variables through a specific memory address value;
A little explanation of scenario 3
((struct node_t *) 0)->c
We access the structure body struct node_t member variable C through the memory address 0来, but there are a few things to explain here:
1. We do not have any memory-related operations on memory address 0, such as dereference, assignment, etc., that is, memory address number 0 begins a section of memory without any changes;
2. We only use the compiler's characteristics to help us compute the structure of the migration, only to use the characteristics of the compiler to calculate it;
3. Good at using some of the compiler features to optimize our program or system;
Conclusion
This paper introduces several ways of accessing structural member variables in C language, and explains the direct access to structural member variables through the memory address value, and explains one of the questions that may arise in the previous blog post.