Recently, while writing a small tool in C, I needed to obtain the offset of structure member variables within the entire structure. The first method that came to mind was to directly calculate the difference based on pointer characteristics. However, I discovered a second method using a standard C operator to obtain the offset, similar to sizeof. Both methods are commonly used and standard.
Method 1: Using Pointer Difference Calculation (Universal and Intuitive)
Principle: By calculating the difference between the address of the structure (usually set to 0) and the address of the member, the offset can be directly obtained. Example code:
#include <stdio.h>
// Define a sample structure
typedef struct {
char a; // 1 byte
int b; // 4 bytes (may have memory alignment)
short c; // 2 bytes
} TestStruct;
int main() {
// Assume the starting address of the structure is 0 (achieved through type casting)
TestStruct *ptr = (TestStruct *)0;
// Member address - structure starting address (0) = offset
size_t offset_a = (size_t)&(ptr->a);
size_t offset_b = (size_t)&(ptr->b);
size_t offset_c = (size_t)&(ptr->c);
printf("Offset of a: %zu\n", offset_a); // Output: 0 (the first member starts from 0)
printf("Offset of b: %zu\n", offset_b); // Output: 4 (due to memory alignment, 3 bytes are added after char)
printf("Offset of c: %zu\n", offset_c); // Output: 8 (int occupies 4 bytes, from 4 to 7, short starts from 8)
return 0;
}
Method 2: Using the Standard Macro offsetof (Recommended, More Concise)
The C standard library (stddef.h) provides the offsetof macro, specifically designed to calculate the offset of structure members. Its implementation principle is consistent with Method 1, but it is more concise and less error-prone. Syntax: offsetof(structure type, member name) Example code:
#include <stdio.h>
#include <stddef.h> // Include offsetof macro
typedef struct {
char a;
int b;
short c;
} TestStruct;
int main() {
size_t offset_a = offsetof(TestStruct, a);
size_t offset_b = offsetof(TestStruct, b);
size_t offset_c = offsetof(TestStruct, c);
printf("Offset of a: %zu\n", offset_a); // 0
printf("Offset of b: %zu\n", offset_b); // 4 (result of memory alignment)
printf("Offset of c: %zu\n", offset_c); // 8
return 0;
}
Summary
Memory alignment affects: The offset can be influenced by the compiler’s memory alignment rules (for example, padding bytes may be added after char to ensure that types like int are aligned to 4 bytes). In the above example, the offset of b is 4 instead of 1, due to memory alignment.
Advantages of offsetof: There is no need to manually handle pointer conversions, and the standard library ensures cross-platform compatibility, making it the recommended approach.
Application scenarios: Commonly used in memory pools, serialization/deserialization, hardware register mapping, and other scenarios that require direct manipulation of memory addresses.
Using the above two methods, the offset of structure members can be accurately obtained, with the offsetof macro being the most convenient and standardized choice.
So, how about it? Did you know about the second method using offsetof?