offsetof宏的实现

指向结构体的指针:

#include <stdio.h>
int main(){
    struct{
        char *name;  //姓名
        int num;  //学号
        int age;  //年龄
        char group;  //所在小组
        float score;  //成绩
    } stu1 = { "Tom", 12, 18, 'A', 136.5 }, *pstu = &stu1;

    //读取结构体成员的值
    printf("%s的学号是%d,年龄是%d,在%c组,今年的成绩是%.1f!\n", (*pstu).name, (*pstu).num, (*pstu).age, (*pstu).group, (*pstu).score);
    printf("%s的学号是%d,年龄是%d,在%c组,今年的成绩是%.1f!\n", pstu->name, pstu->num, pstu->age, pstu->group, pstu->score);

    return 0;
}

C语言结构体指针(指向结构体的指针)详解 (biancheng.net)icon-default.png?t=N7T8https://c.biancheng.net/view/2033.html 结构体名存放的是结构体内成员的第一个元素的首地址。

C语言--结构体内存对齐规则_结构体对齐原则-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/weixin_45157820/article/details/112755832C语言结构体对齐规则:
        结构体(struct)的数据成员,第一个数据成员存放的地址为结构体变量偏移量为0的地址处.

        其他结构体成员自身对齐时,存放的地址为min{有效对齐值为自身对齐值, 指定对齐值} 的最小整数倍的地址处.
注:自身对齐值:结构体变量里每个成员的自身大小
注:指定对齐值:有宏 #pragma pack(N) 指定的值,这里面的 N一定是2的幂次方.如1,2,4,8,16等.如果没有通过宏那么在32位Linux主机上默认指定对齐值为4,64位的默认对齐值为8,AMR CPU默认指定对齐值为8;
        注:有效对齐值:结构体成员自身对齐时有效对齐值为自身对齐值与指定对齐值中 较小的一个.

        总体对齐时,字节大小是min{所有成员中自身对齐值最大的, 指定对齐值} 的整数倍.

#pragma pack(N) 每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数)。程序员可以通过预编译命令#pragma pack(n),n=1,2,4,8,16来改变这一系数,其中的n就是你要指定的“对齐系数”。

//此代码在64位Linux下编写
typedef struct _st_struct1
 {
	char 	a;
	short   b;
	int		c;
 }st_struct1;
 
 printf("%ld\n",sizeof(st_struct1));
打印结果为:8. 让我们分析一下为什么结果为8.
a是char类型,占1个字节.第一个数据成员,放在结构体变量偏移量为0 的地址处.
b是short类型,占2个字节,接下来我们要看结构体对齐规则,b的有效对齐值为min{2, 8}=2. 依次查看2的整数倍地址是否可以存放俩个字节.2×0=0 //此地址处已经存放a. 2×1=2 //此地址为空,我们将占有俩个字节的b存放在地址偏移量为2和3处.
c是int类型,占4个字节,我们根据结构体对齐规则可知,c的有效对齐值为4.对齐到4的整数倍地址,即地址偏移量为4处.在内存中存放的位置为4,5,6,7.
结构体总对齐字节大小为min{4, 8}=4 的整数倍.此时内存中共占8个字节,正好是4的整数倍,所以sizeof(st_struct1)=8.

占用内存空间如下:

举例说明:

结构体内存对齐(如何计算结构体的大小)_按照结构体在内存中的对齐规则,下列结构体类型变量占用内存的大小为( ) struct te-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/chenlong_cxy/article/details/114332324

struct S
{
    double d;
    char c;
    int i;
};

 

写一个宏,计算结构体中某变量相对于首地址的偏移:

#include<stdio.h>
#define OFFSETOF(s_name,m_name)  (int)&(((s_name*)0)->m_name)
struct S
{
	char c;
	int b;
	double a;
};
int main()
{
	printf("%d\n", OFFSETOF(struct S, c));
	printf("%d\n", OFFSETOF(struct S, b));
	printf("%d\n", OFFSETOF(struct S, a));
	return 0;
}

C语言:模拟实现一个offsetof宏,计算结构体中某变量相对于起始地址的偏移_c语言offsetof-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/KamikazePilot/article/details/123303367?spm=1001.2101.3001.6650.2&utm_medium=distribute.pc_relevant.none-task-blog-2~default~BlogOpenSearchComplete~Rate-2-123303367-blog-137144147.235%5Ev43%5Epc_blog_bottom_relevance_base1&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~BlogOpenSearchComplete~Rate-2-123303367-blog-137144147.235%5Ev43%5Epc_blog_bottom_relevance_base1&utm_relevant_index=3

为了更好地理解offset宏的魔力,进一步来看定义的细节,宏中的各种运算符按顺序计算,以便执行以下步骤:

  • ((s *)0):  取整数零并将其转换为指向 s 的指针。
  • ((s *)0)->m: 引用指向结构成员 m 的指针。
  • &(((s *)0)->m):计算 m 的地址。
  • (size_t)&(((s *)0)->m): 将结果转换为适当的数据类型。

根据定义,结构本身驻留在地址 0。因此,指向的字段(上述步骤 3)的地址必须是结构开头的偏移量(以字节为单位)

        offsetof宏是一个预处理器指令,其功能是计算给定结构体类型中特定成员的偏移量(即字节距离)。

        这里的(type*)0是一个技巧性的用法,它将0强制转换为指向type类型结构体的指针。由于0是一个常数,实际上并不会去访问任何实际的内存地址,而是利用这个“伪”指针来获取成员n的地址。接着通过取地址符&得到成员n的地址,最后转换成整型值表示偏移量。

C语言中的offsetof宏计算结构体中某变量相对于首地址的偏移-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/2302_78381559/article/details/137144147?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_baidulandingword~default-1-137144147-blog-135904363.235%5Ev43%5Epc_blog_bottom_relevance_base1&spm=1001.2101.3001.4242.2&utm_relevant_index=4((s *)0):强制转化成数据结构指针,并使其指向地址0;
((s *)0)->m:使该指针指向成员m
&(((s *)0)->m):获取该成员m的地址
(size_t)&(((s *)0)->m):转化这个地址为合适的类型

(size_t)&(((s *)0)->m)的理解_size().gt什么意思-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/lwing25/article/details/4776379?spm=1001.2101.3001.6650.5&utm_medium=distribute.pc_relevant.none-task-blog-2~default~BlogCommendFromBaidu~Rate-5-4776379-blog-93141581.235%5Ev43%5Epc_blog_bottom_relevance_base1&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~BlogCommendFromBaidu~Rate-5-4776379-blog-93141581.235%5Ev43%5Epc_blog_bottom_relevance_base1&utm_relevant_index=6

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: C 语言中没有内置的反射机制,但是你可以使用一些预处理器指令来实现类似的功能。 例如,你可以使用 `#define` 来定义一个类似于反射的,然后在结构体中使用该来生成代码。 例如: ``` #define REFLECT(x) int x##_offset = offsetof(struct_name, x) struct struct_name { int a; REFLECT(a); char b; REFLECT(b); }; ``` 在这个例子中,`REFLECT(a)` 会生成一个 `a_offset` 变量,并将其赋值为 `a` 字段在结构体中的偏移量。你可以使用该变量来获取结构体中的字段,就像使用反射一样。 当然,这只是一种简单的实现方式,你也可以使用其他的技术来实现更复杂的反射功能。 ### 回答2: 在C语言中,没有直接支持结构体的反射。反射是指在程序运行时获取对象的信息和操作对象的能力。C语言是一种静态类型语言,因此在编译时需要明确知道对象的类型和属性,无法在运行时动态获取。 但是,我们可以通过一些技巧来模拟结构体的反射。一个常见的方法是使用定义来定义结构体及其属性,然后使用预处理指令来生成所需的代码。 以一个简单的例子来说明,假设有一个名为Person的结构体,包含name和age两个属性。我们可以定义一个来生成访问这两个属性的函数: ```c #include <stdio.h> #define DEFINE_PERSON_STRUCT \ typedef struct { \ char* name; \ int age; \ } Person; \ #define GET_NAME(person) (person.name) #define GET_AGE(person) (person.age) int main() { Person person; person.name = "Alice"; person.age = 25; printf("Name: %s\n", GET_NAME(person)); printf("Age: %d\n", GET_AGE(person)); return 0; } ``` 在上述代码中,我们使用定义了Person结构体及其属性,然后定义了两个来获取name和age属性的值。在main函数中,我们创建了一个Person结构体对象person,并为其属性赋值,然后使用来获取属性值并进行打印。 通过这种方法,我们可以在C语言中模拟结构体的反射。但需要注意的是,这种方法需要提前定义好所有可能的结构体及其属性,并使用预处理指令来生成代码,不具有动态性。 ### 回答3: C语言本身并没有提供结构体的反射机制,但可以通过一些技巧来实现结构体的反射。 一种实现方式是使用定义来模拟反射。首先,我们可以定义一个来定义结构体,该可以在结构体中添加一个成员变量用于表示结构体的类型。例如: ```c #define REFLECT_STRUCT(type) \ typedef struct type##_struct { \ int type; \ /* other members... */ \ } REFLECT_STRUCT(Person); ``` 然后,我们可以使用另一个来为结构体增加一个getter函数,通过该函数可以获取结构体的类型。例如: ```c #define GET_STRUCT_TYPE(ptr) (ptr->type) int getPersonType(Person* person) { return GET_STRUCT_TYPE(person); } ``` 这样,我们就可以通过调用`getPersonType`函数来获取`Person`结构体的类型。 另一种实现方式是使用代码生成工具,例如使用脚本语言编写一个根据结构体定义生成反射代码的工具。该工具可以读取结构体定义的源文件,然后根据结构体的成员生成对应的getter函数和类型信息。生成的代码可以包含一个`getStructType`函数来获取结构体的类型。 这样,我们在使用结构体时就可以通过调用`getStructType`函数来获取结构体的类型。 总结来说,C语言本身并没有提供直接的结构体反射机制,但我们可以使用定义或代码生成工具来模拟实现反射。这些方法可以通过一些技巧来获取结构体的类型信息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值