代码改变世界

.net中处理C/C++中的位域

2009-08-01 16:34  老羽  阅读(515)  评论(2编辑  收藏  举报

网上搜索了一些资料,发现多数都是把C/C++中的位域当枚举([Flags]enum)处理,个人觉得能做枚举处理的应该是比较特殊的情况,有些情况肯定无法用枚举处理。比如以下情况,是某厂家获取GPS的一个结构体:

//GPS卫星的时间(UTC)
typedef struct _GPS_UTC_Time
{
    unsigned int iYear:12;            // 年?
    unsigned int iMonth:4;            // 月?
    unsigned int iDay:9;              // 日?
    unsigned int iHour:5;             // 小?时?
    unsigned int iMinute:7;           // 分?钟?
    unsigned int iSeconds:7;          // 秒?
}GPS_UTC_Time, *PGPS_UTC_Time;

//卫星信息
typedef struct  _GPS_SATELLITE_INFO
{
    unsigned int id    :5;            // 卫?星?编?号?
    unsigned int angleV:7;            // 卫?星?仰?角?
    unsigned int angleH:9;            // 卫?星?方?位?角?
    unsigned int sar   :7;            // 卫?星?心?噪?比?
}GPS_SATELLITE_INFO, *PGPS_SATELLITE_INFO;

以上2个结构体,在.net中如果声明为枚举类型,肯定无法满足要求,因为结构体中的属性值都不是固定值;我按以下思路处理:

1.先在编译器中查看结构体的长度,因为C/C++存在内存对齐,所以需要查看真实的长度;

int size1=sizeof(GPS_UTC_Time);
int size2=sizeof(GPS_DATA_INFO);
printf("%d %d",size1,size2);//输出结果:4 8 

2.声明.net结构体,并进行运算,计算结构体位域的值 

因为是读取GPS,所以用ulong(8字节)存储GPS_UTC_Time的值,然后通过属性访问真实的结构体;

[StructLayout(LayoutKind.Sequential)]
    public struct GPS_DATA_INFO
    {
        const int GPS_MAX_SATELLITES_NUM = 12;

        //......其余字段略......
ulong gUTCTime; // 卫星时间 public DateTime UTCTime { get { if (gUTCTime > 0) { return new GPS_UTC_Time(gUTCTime).ToDateTime(); } return DateTime.Now; } }
   }     
    public struct GPS_UTC_Time
    {
        public GPS_UTC_Time(ulong value)
        {
            //    5      7       7         9        4         12  
            iYear = (uint)((value << (64 - 12)) >> (64-12));
            iMonth = (uint)((value << (64 - (12 + 4))) >> (64 - 4));
            iDay = (uint)((value << (64 - (12 + 4+ 9))) >> (64 - 9));

            iHour = (uint)((value << (64 - (12 + 4 + 9 + 7))) >> (64 - 7)); //位域是5位,实际是7位,可能是对 iHour字节对齐;
              iMinute = (uint)((value << (64 - (12 + 4 + 9 + 7 + 7))) >> (64 - 7));
            iSeconds = (uint)((value << (64 - (12 + 4 + 9 + 7 + 7 + 7))) >> (64 - 7)); //奇怪的是,这里不需要对齐;
        }

        public DateTime ToDateTime()
        {
            if (iYear > 0 && iMonth > 0 && iDay > 0)
            {
                return new DateTime((int)iYear, (int)iMonth, (int)iDay,
                                (int)iHour, (int)iMinute, (int)iSeconds).ToLocalTime();
            }
            return DateTime.Now;
        }

        uint iYear;     //12 年
         uint iMonth;    //4 月
         uint iDay;      //9 日
         uint iHour;     //5 小时
         uint iMinute;   //7 分钟
         uint iSeconds;  //7 秒
    }
 

 3.分析过程

计算位域时,通过位移运算,首先清除高位,然后清除不需要的低位,得到需要的位域;以一个测试过程为例:

GPS_UTC_Time time;
    time.iYear=2009;
    time.iMonth=11;
    time.iDay= 20;
    time.iHour=13;
    time.iMinute = 16;
    time.iSeconds=8;
    
    byte buffer[8]={0};
    memcpy(buffer,(byte*)&time,8);
 
(1).得到buffer的值为:D9 B7 14 1A 10 04 00 00;因为是低位在前,高位在后,所以倒换次序后为:00 00 04 10 1a 14 b7 d9;
(2).转成为二进制后就可以很直观的看到数据存储的位置及值:

位域:              7            7          2    5             9             4               12       
二进制:    0001000  0010000 00 01101 000010100 1011  011111011001
 值:                 8          16              13             20         11             2009

(3).位运算。

ulong value = 0x000004101a14b7d9; // 00 00 04 10 1a 14 b7 d9

iYear占12位,第一步左移 value << (64 - 12)  ; 得到 0x7d90000000000000; 然后 >> (64-12),将高位移到低位:0x00000000000007d9;即2009,得到正确的值;

其余位域依次类推。。。。


作者:老羽Michael-Zhangyu's Blog
出处:http://michael-zhangyu.cnblogs.com/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。