C - 計算結構體成員偏移量

c語言-計算結構體成員偏移量

轉載至:
http://www.runoob.com/cprogra...
https://blog.csdn.net/encoura...html

1、C 庫宏 - offsetof()

描述

C 庫宏 offsetof(type, member-designator) 會生成一個類型爲 size_t 的整型常量,它是一個結構成員相對於結構開頭的字節偏移量。成員是由 member-designator 給定的,結構的名稱是在 type 中給定的。佈局

聲明

下面是 offsetof() 宏的聲明。.net

offsetof(type, member-designator)

參數

  • type -- 這是一個 class 類型,其中,member-designator 是一個有效的成員指示器。
  • member-designator -- 這是一個 class 類型的成員指示器。

返回值

該宏返回類型爲 size_t 的值,表示 type 中成員的偏移量。指針

實例

下面的實例演示了 offsetof() 宏的用法。code

#include <stddef.h>
#include <stdio.h>

struct address {
   char name[50];
   char street[50];
   int phone;
};
   
int main()
{
   printf("address 結構中的 name 偏移 = %d 字節。\n",
   offsetof(struct address, name));
   
   printf("address 結構中的 street 偏移 = %d 字節。\n",
   offsetof(struct address, street));
   
   printf("address 結構中的 phone 偏移 = %d 字節。\n",
   offsetof(struct address, phone));

   return(0);
}

讓咱們編譯並運行上面的程序,這將產生如下結果:htm

address 結構中的 name 偏移 = 0 字節。
address 結構中的 street 偏移 = 50 字節。
address 結構中的 phone 偏移 = 100 字節。

宏實現解析

問題

寫一個宏計算出結構體成員的偏移量。
假設有以下一個結構體,要計算成員c的在結構體中的偏移量。blog

typedef struct Type_t{
    char a;  // 0
    int b;   // 4~7
    double c; // 8~16
};

注意,上述的結構體必須考慮字節對齊的問題。內存

方法一

咱們能夠聲明一個Type_t結構的變量type,而後將成員c的地址減去成員a的地址就是c的偏移量了。get

Type_t type;
offset = (unsigned long)(&(type.c)) - (unsigned long)(&(type.a)); // 其中,(&(type.a)) 與 (&(type))是等價的

將上面整理爲宏就是:編譯器

#define OFFSET(TYPE, MEMBER, OFF) \
    TYPE temp;              \
    OFF = (unsigned long)(&(temp.MEMBER)) - (unsigned long)(&(temp));

這個宏建立了一個類型爲TYPE的臨時變量temp,而後求出MEMBER成員的偏移量放在OFF裏。

完整代碼以下:

#include <stdio.h>
#include <string.h>

#define OFFSET(TYPE, MEMBER, OFF) \
    TYPE temp;              \
    OFF = (unsigned long)(&(temp.MEMBER)) - (unsigned long)(&(temp));

typedef struct Type_t{
    char a;
    int b;
    double c;
};


int main(void)
{
    int offset = 0;
    Type_t type;

    offset = (unsigned long)(&(type.c)) - (unsigned long)(&(type));
    OFFSET(Type_t, c, offset);
    printf("offset = %d\n", offset);
    getchar();
}

方法二

若是可以讓(unsigned long)(&(type))的值爲0,即&(type) == 0的時候,那麼offset的值就是簡單的:

offset = (unsigned long)(&(type.c));

若是說&(type) == 0,那麼type.c就能夠等價於((Type_t *)0)->c。可是這個語句是不能單獨存在的,由於對NULL指針訪問成員c是非法的。能夠經過在該語句以前加上&符號,即獲取成員c的地址就沒問題了。所以,對應的宏以下:

#define OFFSET(TYPE, MEMBER) ((unsigned long)(&(((TYPE *)0)->MEMBER)))

總結

ANSI C標準容許任何值爲0的常量被強制轉換成任何一種類型的指針,而且轉換結果是一個NULL指針,所以((Type_t)0)的結果就是一個類型爲Type_t的NULL指針。若是利用這個NULL指針來訪問Type_t的成員固然是非法的,但&(((Type_t)0)->c)的意圖並不是想存取c字段內容,而僅僅是計算當結構體實例的首址爲((Type_t)0)時c字段的地址。聰明的編譯器根本就不生成訪問m的代碼,而僅僅是根據Type_t的內存佈局和結構體實例首址在編譯期計算這個(常量)地址,這樣就徹底避免了經過NULL指針訪問內存的問題。

相關文章
相關標籤/搜索