zoukankan      html  css  js  c++  java
  • 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
    ", &p);    
        printf("header field's address id: 0x%x
    ", &p.header);
    
        printf("---------------------------------
    ");
    
        printf("crc32 field's address: 0x%x
    ", &p.crc32);
    
        int offset = (unsigned char *)&p.crc32 - (unsigned char *)&p;
    
        printf("offset is: %d
    ", offset);
    
        printf("-----------------------------------------------------------
    ");
    
        printf("crc32 offset: %d
    ", 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语言代码维护起来也是非常省心的。

     
  • 相关阅读:
    对文件上传使用表单验证
    文件上传
    自定义验证器
    WTForms常用的验证器
    Eclipse自动补全+常用快捷键
    JNI笔记
    cocos2d 2.2.6 win7下的配置
    cocos2d 3.6 win7下的配置
    python--文件删除、判断目录存在、字符串替换
    只是一个文件节点类为了项目的数据处理
  • 原文地址:https://www.cnblogs.com/yongdaimi/p/14480391.html
Copyright © 2011-2022 走看看