C 语言中同意将值为 0 的变量强制转换成任一类型的指针,转换结果是一个NULL指针。

(type*)0 // 一个 type 类型的NULL指针

用这个指针訪问结构体内的成员是非法的,可是

&(((type*)0)->field)

是为了计算 field 的地址 ,编译器不会产生訪问 field 的代码。仅仅会依据 type 的布局和起始地址在编译期计算这个地址(常量)。而又由于初始地址为 0,故该地址的值就是该结构体成员相对于结构体基址的偏移。

(size_t)&(((type*)0)->field)

结构体的 sizeof 的相关知识:

对 32 位操作系统。默认按 4 字节对齐;

sizeof(union),结果是 union 占用内存最大成员的大小(各成员共享内存)

编译器是依照什么样的原则进行对齐的?

  • 1.数据类型自身的对齐值:

    对于char型数据,其自身对齐值为1,对于short型为2,对于int, float类型,其自身对齐值为4,对于double类型其自身对齐值为8,单位字节。

  • 2.结构体或者类的自身对齐值:其成员中自身对齐值最大的那个值。

  • 3.指定对齐值:#pragma pack (value)时的指定对齐值value。

  • 4.数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中小的那个值。

  • 5.对齐后的长度必须是成员中最大的对齐參数的整数倍
#include <iostream>
using namespace std;
#define ISUNSIGNED(x) ((x > 0) && (~x > 0))
#define OffSet(type, field) ((size_t)&(((type*)0)->field))
int main()
{
struct A{
char a;
int b;
float c;
double d;
char e;
union B{
char f[100];
struct C{
int g[50];
} h;
}i;
bool j;
} s;
cout << sizeof(A) << endl; // 232
cout << "a 的起始偏移:"<< OffSet(A, a) << endl; // 0
cout << "b 的起始偏移:"<< OffSet(A, b) << endl; // 4
cout << "c 的起始偏移:"<< OffSet(A, c) << endl; // 8
cout << "d 的起始偏移:"<< OffSet(A, d) << endl; // 16
cout << "e 的起始偏移:"<< OffSet(A, e) << endl; // 24
cout << "i 的起始偏移:"<< OffSet(A, i) << endl; // 28
cout << "j 的起始偏移:"<< OffSet(A, j) << endl; // 228 int p = 1;
unsigned int q = 1;
cout << p << " " << ~p << endl; // 1 -2
cout << q << " " << ~q << endl; // 1 4294967294
cout << "p是否是无符号数:" << ISUNSIGNED(p) << endl; // 0
cout << "q是否是无符号数:" << ISUNSIGNED(q) << endl; // 1
return 0;
}

以上结构体分析:

struct A{
char a;
int b;
float c;
double d;
char e;
union B{
char f[100];
struct C{
int g[50];
} h;
}i;
bool j;
} s;
  • 结构体 A 的自身对齐值为最大的成员的自身对齐值,为 8 (double d), 注意内部结构体 C 的最大对齐值是 4 (int)。Union B 最大对齐值是 4 (int);
  • 成员char a 的本身对齐值是 1,结构体对齐值是 8。按 1 字节对齐;
  • 成员 int b 的 本身对齐值是 4。结构体对齐值是 8,按 4 字节对齐,它的起始偏移地址为 4。
  • 成员 float c 的 本身对齐值是 4,结构体对齐值是 8,按 4 字节对齐。它的起始偏移地址为 8。
  • 成员 double d 的 本身对齐值是 8,结构体对齐值是 8。按 8 字节对齐,它的起始偏移地址为 16;
  • 成员 char e 的 本身对齐值是 1,结构体对齐值是 8,按 1 字节对齐。它的起始偏移地址为 24;(16+8)
  • 成员 Union B i 的 本身对齐值是 4,结构体对齐值是 8,按 4 字节对齐。它的起始偏移地址为 28;
  • 成员 bool j 的 本身对齐值是 1,结构体对齐值是 8。按 1 字节对齐。它的起始偏移地址为 228 ;(28+200)
  • 结构体对齐值是 8, 因此结构体 A 的大小为 232 (228+1 补齐成 8 的倍数)

一个成员在内存中补位所占的内存,决定于该成员之后的成员类型。

參考:《程序猿笔试面试宝典》

怎样求结构体成员的偏移地址 || 结构体的 sizeof 总结的更多相关文章

  1. 套接字编程简介: IPV4套接字地址结构/ 通用套接字地址结构/ IPV6套接字地址结构/新通用套接字地址结构

    IPv4套接字地址结构通常也称为“网际套接字地址结构”,它以sockaddr_in命名,定义在<netinet/in.h>头文件中. struct in_addr { in_addr_t ...

  2. C语言根据结构体成员变量的地址,得到结构体的地址

    看nginx代码时发现双链表使用的是这种方法,记录一下 给出一个实例来说明 struct father_t {    int a;    char *b;    double c;}f;char *p ...

  3. 指针直接赋值为整型AND利用宏定义求结构体成员偏移量

    首先我们要更正一个很熟悉的概念,那就是指针不仅仅是“地址”,指针还有一个很重要的特性,那就是“类型”. 指针初始化时,“=”的右操作数; 除外,该语句表示指针为空): 所以 ; 这样的代码是不允许的. ...

  4. 由结构体成员地址计算结构体地址——list_entry()原理详解

    #define list_entry(ptr, type, member) container_of(ptr, type, member) 在进行编程的时候,我们经常在知道结构体地址的情况下,寻找其中 ...

  5. Linux中的两个经典宏定义:获取结构体成员地址,根据成员地址获得结构体地址;Linux中双向链表的经典实现。

    倘若你查看过Linux Kernel的源码,那么你对 offsetof 和 container_of 这两个宏应该不陌生.这两个宏最初是极客写出的,后来在Linux内核中被推广使用. 1. offse ...

  6. 【C语言】这种求结构体成员大小的方法,你可能需要了解一下~

    在C语言编程中,有时候需要知道某结构体中某成员的大小,比如使用堆内存来存储结构体中的某成员时,需要知道该成员的大小,才好确定所需申请的空间大小.求某结构体中某成员的大小,你会怎么做? 例子: type ...

  7. C/C++ 结构体成员在内存中的对齐规则(转载)

    这几天在看王艳平的<windows 程序设计>,第5章讲解了MFC框架是怎么管理窗口句柄到窗口实例之间的映射,用到了两个类CPlex和CMapPtrToPtr,用于管理内存分配的类(避免因 ...

  8. C/C++ 结构体成员在内存中的对齐规则

    这几天在看王艳平的<windows 程序设计>,第5章讲解了MFC框架是怎么管理窗口句柄到窗口实例之间的映射,用到了两个类CPlex和CMapPtrToPtr,用于管理内存分配的类(避免因 ...

  9. 【PE结构】由浅入深PE基础学习-菜鸟手动查询导出表、相对虚拟地址(RVA)与文件偏移地址转换(FOA)

    0 前言 此篇文章想写如何通过工具手查导出表.PE文件代码编程过程中的原理.文笔不是很好,内容也是查阅了很多的资料后整合出来的.希望借此加深对PE文件格式的理解,也希望可以对看雪论坛有所贡献.因为了解 ...

随机推荐

  1. 零基础学Python不迷茫——基本学习路线及教程!

    什么是Python? 在过去的2018年里,Python成功的证明了它自己有多火,它那“简洁”与明了的语言成功的吸引了大批程序员与大数据应用这的注意,的确,它的实用性的确是配的上它的热度.   Pyt ...

  2. 小数据池 is 和 ==的区别

    小数据池 一.小数据池 1)代码块 python程序是由代码块构成的,一个代码块的文本作为pythont程序执行的单元 官方文档: A Python program is constructed fr ...

  3. Django之ORM操作(重要)

    Django ORM操作 一般操作 看专业的官网文档,做专业的程序员! 必知必会13条   <1> all(): 查询所有结果 <2> get(**kwargs): 返回与所给 ...

  4. debian 9 安装AMD驱动

    目录 debian 9 安装AMD驱动 安装驱动之前: 安装驱动: 安装驱动之后: debian 9 安装AMD驱动 需求说明: 安装完成debian系统后独显驱动未安装 操作系统版本: kyeup@ ...

  5. NYOJ 747 蚂蚁的难题(三)

    蚂蚁的难题(三) 时间限制:2000 ms  |  内存限制:65535 KB 难度:4   描述 蚂蚁终于把尽可能多的食材都搬回家了,现在开始了大厨计划. 已知一共有 n 件食材,每件食材有一个美味 ...

  6. php 投票

    1.投票主界面(问题界面) <?php$db = new Mysqli("localhost","root","root"," ...

  7. 使用Unity做2.5D游戏教程(一)

    最近在研究Unity 3D,看了老外Marin Todorov写的教程很详细,就翻译过来以便自己参考,翻译不好的地方请多包涵. 如果你不了解2.5D游戏是什么,它基本上是个3D游戏而你可以想象是压扁的 ...

  8. spring之生命周期

    1.容器中对的生命周期 spring可以管理 singleton作用域的bean的生命周期,spring可以精确地知道该bean何时被创建,何时被初始化完成,容器合适准备销毁该bean实例. spri ...

  9. Spring boot 中 Spring Security 使用改造5部曲(转)

    文章的内容有点长,也是自己学习Spring security的一个总结.如果你从头看到尾,我想你对Spring Security的使用和基本原理应该会有一个比较清晰的认识. 如果有什么理解不对的地方, ...

  10. LA 4728 旋转卡壳算法求凸包的最大直径

    #include<iostream> #include<cstdio> #include<cmath> #include<vector> #includ ...