C語言:計算結構體偏移量的一個小技巧


一. 概述

經常會遇到計算結構體偏移量的需求, 比如有下面這樣一個結構體:

struct mav_protocol
{
    char       header;
    char       seq;
    short      command_id;
    char       payload[256];
    int        crc32;
} p;

需要在傳輸到對端前填入它的crc32值,以確保對端在收到這組數據后能夠根據填入的crc32值判斷收到的這組數據是否仍然正確。

那一般都會有一個公共的函數去計算結構體里某段數據的crc值,函數原型大概如下:

int get_crc32(char *buf, int size)
{
    // 計算CRC...    
    return 0;
}

需要傳入一個數據指針以及需要計算數據的偏移量,對於我們這個例子來說,需要傳入的數據指針就是  struct mav_protocol * , 偏移量就是結構體里crc32這個成員之前的所有數據的長度,也就是 header, seq, command_id...., 等等數據需要參與crc計算。一般計算偏移量的做法是 :

int offset = (unsigned char *)&p.crc32 - (unsigned char *)&p;

然后再代入上面的那個計算crc32的函數:

get_crc32((char *)&p, offset);

今天發現了另一種更巧妙的寫法:

二. 具體寫法

我看到這哥們直接定義了一個宏:

#define offset(type, v) (&(((type *)0)->v))

然后在使用時直接使用  offset(struct mav_protocol, crc32)  來拿到 crc32這個結構體成員在結構中的偏移量,剛開始很疑惑這種寫法,后來反復看了幾次之后明白了其中的妙處:

簡單的說,既然結構體成員的地址減去結構體的地址就等於該成員的偏移量,那如果結構體的地址為0,該成員的地址就恰好等於它在結構體中的偏移量了。

下面做一個實驗來驗證這個寫法是否正確:

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

#define offset(type, v) (&(((type *)0)->v))

int get_crc32(char *buf, int size);

struct mav_protocol
{
    char       header;
    char       seq;
    short      command_id;
    char       payload[256];
    int        crc32;
} p;


int main()
{

    printf("struct p's address is: 0x%x\n", &p);    
    printf("header field's address id: 0x%x\n", &p.header);

    printf("---------------------------------\n");

    printf("crc32 field's address: 0x%x\n", &p.crc32);

    int offset = (unsigned char *)&p.crc32 - (unsigned char *)&p;

    printf("offset is: %d\n", offset);

    printf("-----------------------------------------------------------\n");

    printf("crc32 offset: %d\n", offset(struct mav_protocol, crc32));

    return 0;
}

運行:

struct p's address is: 0x407040

header field's address id: 0x407040

---------------------------------

crc32 field's address: 0x407144

offset is: 260

-----------------------------------------------------------

crc32 offset: 260

可以看到,不管是使用普通的做法還是使用宏的做法得到的結果都是一致的,這樣以后需要計算任意結構體成員的偏移量都可以通過這個宏,只傳一個結構名和一個結構體成員名就可以了。

而且,無論以后如何調整這個結構體的成員,刪除也好,新增也罷,只要保證crc32是它的最后一個成員,計算校驗值的代碼就無需改動,這樣的C語言代碼維護起來也是非常省心的。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM