zoukankan      html  css  js  c++  java
  • offsetof(s,m)解析

    https://www.cnblogs.com/jingzhishen/p/3696293.html

    sizeof()用法汇总


    sizeof()功能:计算数据空间的字节数
    1.与strlen()比较
          strlen()计算字符数组的字符数,以""为结束判断,不计算为''的数组元素。
          而sizeof计算数据(包括数组、变量、类型、结构体等)所占内存空间,用字节数表示。
    2.指针与静态数组的sizeof操作
          指针均可看为变量类型的一种。所有指针变量的sizeof 操作结果均为4。
    注意:int *p; sizeof(p)=4;
                      但sizeof(*p)相当于sizeof(int);      
          对于静态数组,sizeof可直接计算数组大小;
          例:int a[10];char b[]="hello";
                  sizeof(a)等于4*10=40;
                  sizeof(b)等于6;
     注意:数组做型参时,数组名称当作指针使用!!
                   void  fun(char p[])
                   {sizeof(p)等于4}

        char str[20]="0123456789";
        int a=strlen(str); //a=10;
        int b=sizeof(str); //而b=20;    
        char ss[] = "0123456789";    
        sizeof(ss) 结果 11 ===》ss是数组,计算到位置,因此是10+1   
        sizeof(*ss) 结果 1 ===》*ss是第一个字符
        char ss[100] = "0123456789";
        sizeof(ss) 结果是100 ===》ss表示在内存中的大小 100×1
        strlen(ss) 结果是10 ===》strlen是个函数内部实现是用一个循环计算到为止之前
        int ss[100] = "0123456789";
        sizeof(ss) 结果 400 ===》ss表示在内存中的大小 100×4
        strlen(ss) 错误 ===》strlen的参数只能是char* 且必须是以''结尾的
        char q[]="abc";
        char p[]="a ";
        sizeof(q),sizeof(p),strlen(q),strlen(p);     结果是 4 3 3 2
        
         一些朋友刚开始时把sizeof当作了求数组元素的个数,如今你应该知道这是不对的,那么应该怎么求数组元素的个数呢Easy,通常有下面两种写法:
         1.int c1 = sizeof( a1 ) / sizeof( char );         // 总长度/单个元素的长度
         2.int c2 = sizeof( a1 ) / sizeof( a1[0] );        // 总长度/第一个元素的长度    
     
         Student stus[4]={
            {"humingtao",24,99},
            {"zhaoweisong",23,66},
            {"chenfengchang",21,88},
            {"zhuwenpeng",20,77}
            
         数组长度  count = sizeof(stus)/sizeof(stus[0]
     
         
         double* (*a)[3][6];
         cout<<sizeof(a)<<endl; // 4 a为指针
         cout<<sizeof(*a)<<endl; // 72 *a为一个有3*6个指针元素的数组
         cout<<sizeof(**a)<<endl; // 24 **a为数组一维的6个指针
         cout<<sizeof(***a)<<endl; // 4 ***a为一维的第一个指针
         cout<<sizeof(****a)<<endl; // 8 ****a为一个double变量

    offsetof(s,m)解析

     
    使用实例:
    typedef struct {
        const AVClass *class; 
        char *expr_str;
        AVExpr *expr;
        double var_values[VAR_VARS_NB];
        enum AVMediaType type;
    } SetPTSContext;
     
     
     
    #define OFFSET(x) offsetof(SetPTSContext, x)
    #define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_FILTERING_PARAM
    static const AVOption options[] = { 
        { "expr", "Expression determining the frame timestamp", OFFSET(expr_str), AV_OPT_TYPE_STRING, { .str = "PTS" }, .flags = FLAGS },
        { NULL }
    };

     
     
    http://blog.chinaunix.net/uid-13701930-id-336445.html
     
    offsetof 求某个结构体的特定成员在结构体里面的偏移量
     
    (s *)0 是骗编译器说有一个指向类(或结构)s的指针,其值为0   
    &((s *)0)->m   是要取得类s中成员变量m的地址   
    由于这个类(或结构)的基址为0,这时m的地址当然就是m在s中的偏移了
     
    (s *)0 是把0地址转换为s指针类型,然后从这个指针上“取”m成员再取址,而m成员的地址转换后结果就是m成员相对于整个对象的偏移量(我们既然是从0地址开始算的,就不用再减去起始地址0)。
     
    在嵌入式应用中,或许你对offsetof接触不多甚至根本没见过。如果是这样,那么从这一刻起就好好地掌握它,让它成为你的又一杀手锏吧。
    1. offsetof与EEPROM
      我们许多人可能都使用过一些非挥发性的存储器,如常见的EEPROM。我们经常使用它们在存储一些系统的配置参数和设备信息。在所有的EEPROM中,通过串口访问的占了大多数。一般来说,对串口的访问都是按字节进行的,这使得我们不可避免会设计出下面的
    接口去访问EEPROM的信息:
    /*从EEPROM 偏移量offset处读取nBytes到RAM地址dest*/
    ee_rd(uint16_t offset, uint16_t nBytes, uint8_t * dest);
    然而,这种接口必须要知道偏移量offset和读取字节数nBytes。可能你会采用下面的方法解决方法解决这个问题:
    定义一个数据结构和一个指向这个数据结构的指针,并初始化这个指针为EEPROM的起始地址EEPROM_BASE.
    ----------------------------  <-EPPROM_BASE:0x0000000     
    i | f | c |   |   |   |...
    ----------------------------         
    |   |   |   |   |   |   |...
    ----------------------------         
    |   |   |   |   |   |   |...
    ----------------------------         
    ...
    ----------------------------
    #define EEPROM_BASE 0x0000000/*配置信息的起始地址*/
    typedef struct
    {   
         int    i;  
         float  f
         char   c
    } EEPROM;
    EEPROM * const pEE = EEPROM_BASE
    ee_rd(&(pEE->f), sizeof(pEE->f), dest);
    没错,这种方法的确可以达到访问指定地址的信息。不过这种方法也存在下面的问题:
    a.容易使代码维护人员人误以为在ee_rd接口内部也存在EEPROM的数据结构。
    b.当你编写一些自己感觉良好编译器不报错的代码,比如pEE->f = 3.2,你可能意想不到灾难将要来临。
    c.这个接口没有很好地体现EEPROM所隐含的硬件特性。
    到这里,有人可能会想到offsetof(那些没用过甚至没见过的朋友别急,后面马上会详解offsetof)来解决这个问题:
    /*offsetof获取数据成员在数据结构中的偏移量
    比如成员f在EEPROM数据结构中的偏移量,这里为什么
    要强制转化0,这是个有深度的问题,在后面也会详细说明*/
    #define offsetof(type, f) ((size_t)
        ((char *)&((type *)0)->f - (char *)(type *)0))
    typedef struct
    {
         int    i; 
         float  f; 
         char   c; 
    } EEPROM;
    ee_rd(offsetof(EEPROM,f), 4, dest);
    如果你能想到这里说明你对offsetof有一定程度的理解,不过还可以改进。如果让编译器来计算nBytes而不是我们自己给出4那就更好 了。这时,一定有人会马上提到sizeof。是的。可是怎么使用呢,我们不能用sizeof(EEPROM.f)来计算nBytes吧?!我想那些对 offsetof有较深理解的同志一定会这么办:
    /*类似于offsetof的定义*/
    #define SIZEOF(s,m) ((size_t) sizeof(((s *)0)->m))
    ee_rd(offsetof(EEPROM, f), SIZEOF(EEPROM, f), &dest);
    很不错! 其实还可以精简为下面的最终形式:
    #define EE_RD(M,D)   ee_rd(offsetof(EEPROM,M), SIZEOF(EEPROM,M), D)
    EE_RD(f, &dest);
    哈哈,这样我们只用传递两个参数,不用再考虑应该从那里读取数据以及读取多少的问题。
    先打住,有人会说这种简化都是建立在EEPROM_BASE为0x0000000基础之上的,可能会反问,如果配置信息不是从0地址开始的呢?

    Good question.其实我们可以通过下面的方法解决。
    #define EEPROM_BASE 0x00000a10
    typedef struct
    {
         char   pad[EEPROM_BASE];/*使数据结构的前EEPROM_BASE个字节填"空"*/ 
         int    i
         float  f
         char   c
    } EEPROM;

    ----------------------------  0x00000000
    |   |   |   |   |   |   |...
    ----------------------------   
    ...
    ---------------------------- <-EPPROM_BASE:0x00000a10             
    i | f | c |   |   |   |...
    ----------------------------         
    |   |   |   |   |   |   |...
    ----------------------------         
    ...
    使用offsetof简化EEPROM的串口访问的确很妙。这里还有一个很好的例子。
    在嵌入式应用中,我们时常将一些I/O寄存器映射到内存地址空间进行访问。
    这种映射使原本复杂的寄存器访问变得象访问普通的RAM地址一样方便。

    在我们视频会议系统中,PowerPC 8250访问外部的ROM控制器(ROM controller)的
    寄 存器就是通过这种方式实现的。ROM控制器所有的寄存器被映射到从I/O寄存器空间基地址0x10000000(IO_BASE)偏移 0x60000(ROMCONOffset)字节的一段内存。每个寄存器占用四个字节,并有一个数据结构与它们对应。比如控制ROM控制器工作状态的寄存 器对应数据结构ROMCON_ROM_CONTROL,配置PCI总线A的寄存器对应数据结构ROMCON_CONFIG_A,下面先看看这些数据结构的定义:
    #define IO_BASE      0x10000000
    #define ROMCONOffset 0x60000
    typedef unsigned int NW_UINT32;
    typedef struct _ROMCON_CONFIG_A {
        union {
            struct {
                UINT32 pad4:21;         /* unused   */
                UINT32 pad3:2;          /* reserved */
                UINT32 pad2:5;          /* unused   */
                UINT32 EnablePCIA:1;
                UINT32 pad1:1;          /* reserved */
                UINT32 EnableBoot:1;         
                UINT32 EnableCpu:1;     /*bit to enable cpu*/
            } nlstruct;
            struct {
                UINT32 ConfigA;
            } nlstruct4;
        } nlunion;
    ROMCON_CONFIG_A, *PROMCON_CONFIG_A;
    typedef struct _ROMCON_ROM_CONTROL {
        union {
            struct {
                UINT32 TransferComplete:1;
                UINT32 pad3:1;            /* unused */
                UINT32 BondPad3To2:2;
                UINT32 Advance:3;
                UINT32 VersaPortDisable:1;
                UINT32 pad2:1;            /* unused */
                UINT32 FastClks:1;
                UINT32 pad1:7;            /* unused */
                UINT32 CsToFinClks:2;
                UINT32 OeToCsClks:2;
                UINT32 DataToOeClks:2;
                UINT32 OeToDataClks:3;
                UINT32 CsToOeClks:2;
                UINT32 AddrToCsClks:2;         
                UINT32 AleWidth:2;
            } nlstruct;
            struct {
                UINT32 RomControl;
            } nlstruct4;
        } nlunion;
    ROMCON_ROM_CONTROL, *PROMCON_ROM_CONTROL;
    typedef struct
    {
        ROMCON_CONFIG_A     ConfigA;
        ROMCON_CONFIG_B     ConfigB;
        ROMCON_ROM_CONTROL  RomControl;
        ...
    }ROMCON, *PROMCON;
    ----------------------------  <-IO_BASE:0x10000000    
    |   |   |   |   |   |   |...
    ----------------------------         
    |   |   |   |   |   |   |...
    ...
    ----------------------------  <-ROMCONOffset(ROMCON):0x60000      
    |   |   |   |   |   |   |...
    ----------------------------  <-ROMCON_ROM_CONTROL             
    ...
    ----------------------------
    那么如何访问ROMCON_ROM_CONTROL对应寄存器呢,比如ROMCON_ROM_CONTROL对应寄存器的VersaPortDisable位?
    估计有人可能会这样做:
    事先定义成员RomControl(ROMCON中用ROMCON_ROM_CONTROL定义的实例)相对与ROMCON的偏移量,
    #define ROMCONRomControlOffset 0x8
    然后设计访问ROM的接口如下:
    /*读取ROM控制器位于src位置的寄存器数据到dest*/
    typedef unsigned long dword_t;

    void rom_read(dword_t* src, uint32_t* dest);
    void rom_write(dword_t* src, uint32_t* dest);
    最后利用这个偏移量做下面的操作:
    ROMCON_ROM_CONTROL tRomCtrl={0};
    dword_t* pReg=(dword_t*)(IO_BASE+ROMCONOffset+
         ROMCONRomControlOffset);
    rom_read(pReg,(uint32_t)*(&tRomCtrl));
    /*查看寄存器的VersaPortDisable位,如果该位没有启用就启用它*/
    if(!tRomCtrl.nlunion.nlstruct.VersaPortDisable)
    {
      tRomCtrl.nlunion.nlstruct.VersaPortDisable = 1;
      rom_write(pReg,(uint32_t)*(&tRomCtrl));
    }
     
    这样做确实可以达到访问相应寄存器的目的。但是,如果和ROM相关的寄存器很多,那么定义、记忆和管理那么多偏移量不是很不方便吗?到这里,如果你对前面关于offsetof还有印象的话,我想你可能会作下面的优化:
    #define ROMCON_ADDR(m)   (((size_t)IO_BASE+
                             (size_t)ROMCONOffset+
                             (size_t)offsetof(ROMCON,m))
    ROMCON_ROM_CONTROL tRomCtrl={0};
    dword_t* pReg=(dword_t*)ROMCON_ADDR(ConfigA);
    rom_read(pReg,(uint32_t)*(&tRomCtrl));
    /*查看寄存器的VersaPortDisable位,如果没有启动就启动它*/
    if(!tRomCtrl.nlunion.nlstruct.VersaPortDisable)
    {
      tRomCtrl.nlunion.nlstruct.VersaPortDisable = 1;
      rom_write(pReg,(uint32_t)*(&tRomCtrl));
    }

    2.offsetof的来龙去脉
      通过前面的举例,你可能对如何使用offsetof已经不陌生了吧。offsetof对那些搞
    C++ 的人可能很熟悉,因为offsetof类似于sizeof,也是一种系统操作符,你不用考虑它是怎么定义的。这个操作符offsetof的定义可以在 ANSI C 编译器所带的stddef.h中找到。在嵌入式系统里,不同开发商,不同架构处理器和编译器都有不同的offsetof定义形式:
    /* Keil 8051 */
    #define offsetof(s,m) (size_t)&(((s *)0)->m)
    /* Microsoft x86 */
    #define offsetof(s,m) (size_t)(unsigned long)&(((s *)0)->m)
    /* Motorola coldfire */
    #define offsetof(s,memb) ((size_t)((char *)&((s *)0)->memb-(char *)0))
    /* GNU GCC 4.0.2 */
    #define offsetof(TYPE, MEMBER) __builtin_offsetof (TYPE, MEMBER)
    虽然定义形式不同,但功能都是返回成员在数据结构中的偏移量,都是为了提高代码的可移植性。
    下面拿KEIL 8051的定义来作点解释:
    ((s *)0):强制转化成数据结构指针,并使其指向地址0;
    ((s *)0)->m:使该指针指向成员m
    &(((s *)0)->m):获取该成员m的地址
    (size_t)&(((s *)0)->m):转化这个地址为合适的类型
    你可能会迷惑,这样强制转换后的结构指针怎么可以用来访问结构体字段?呵呵,其实这个表达式根本没有也不打算访问m字段。ANSI C标准允许任何值为0的常量被强制转换成任何一种类型的指针,并且转换结果是一个NULL指针,因此((s*)0)的结果就是一个类型为s*的NULL指 针。如果利用这个NULL指针来访问s的成员当然是非法的,但&(((s*)0)->m)的意图并非想存取s字段内容,而仅仅是计算当结构 体实例的首址为((s*)0)时m字段的地址。聪明的编译器根本就不生成访问m的代码,而仅仅是根据s的内存布局和结构体实例首址在编译期计算这个(常 量)地址,这样就完全避免了通过NULL指针访问内存的问题。又因为首址的值为0,所以这个地址的值就是字段相对于结构体基址的偏移。
    这里有个地方需要注意:就是offsetof虽然同样适用于union结构,但它不能用于计算位域(bitfield)成员在数据结构中的偏移量。
    typedef struct
    {
      unsigned int a:3;
      unsigned int b:13;
      unsigned int c:16;
    }foo;
    使用offset(foo,a)计算a在foo中的偏移量,编译器会报错。
  • 相关阅读:
    linux系统调用之系统控制
    linux系统调用之文件系统操作
    使用EF框架实现MVC的增删改查功能
    MVC+EF快速弄出一个CRUD
    Entity Framework 全面教程详解(转)
    微信小程序学习
    为Bootstrap模态对话框添加拖拽移动功能
    Razor语法大全
    EXCEL怎么打20位以上的数字?
    C# SQLite 数据库操作学习
  • 原文地址:https://www.cnblogs.com/sundaysgarden/p/10730501.html
Copyright © 2011-2022 走看看