转载自:
http://blog.sina.com.cn/s/blog_4513dde60100o6qn.html
这是调电路时遇到的问题。由12位AD传来的数据放入字长为32b的存储器里。其中存储器的数据线中低12b与AD相连,其余接地。AD数据使用12位补码表示。于是字长为32b的存储器里每个字存放的数据是由低12b的AD数据和高20b的0组成。如果再利用运行于CPU上的C程序对这些存储器中的数据进行处理,则无法直接使用。
例如,AD传送了数据-2,用12b的补码表示就是0xffe,但是在C程序中如果直接用32b的int类型来操作,就成了0xffe == 4094,两者相去甚远。只有进行一些附加的操作,比如,判断数据的11b(最低位为0b)是否等于1,若是,则将数据的高20b置1,写成代码就是:
if ((x & 0x800) != 0) x |= 0xfffff000;
我想,在C的层面上是否有更简便而灵活的操作方法。假如说我们的AD换成了4b,6b,8b,14b,16b,18b,20b等等(好像AD没有这么多类),那么上面代码中的0x800和0xfffff000就要换成相应的数值才行,虽然不很麻烦,但也有点麻烦。
那天看以前写的程序,发现位段结构体中位域都使用unsigned修饰的。我想,位域大概也可以用signed修饰吧!好像从来没有这样做过。如果能行,我们是不是找到了一种应对AD有符号补码数据的灵活方法呢?于是写了一段代码验证想法,如下:
// bitstruct.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include<stdlib.h>
typedef unsigned int UINT;
#define INTBITS(WIDTH) \
typedef struct \
{ \
signed ad : WIDTH; \
unsigned unused : 32 - WIDTH; \
} INT##WIDTH
int _tmain(int argc, _TCHAR* argv[])
{
UINT x;
INTBITS(12); // Now struct INT12 is available
INTBITS(14); // Now struct INT14 is available
x = 0x00000ffe;
printf(
"x == 0x%x\n"
"12 bits signed number of x == %d\n",
x,
(reinterpret_cast<INT12*>(&x))->ad
);
x = 0x00003ffe;
printf(
"x == 0x%x\n"
"14 bits signed number of x == %d\n",
x,
(reinterpret_cast<INT14*>(&x))->ad
);
system("pause");
return 0;
}
程序的结果表明,这种方法的确运行正确。
程序的关键在于宏INTBITS,它有一个参数WIDTH,用来表示AD的位数,可以代入12,14这样的整数,不过千万不要代入12+2这样的表达式。代入不同的整数,INTBITS会定义不同的结构体,INTBITS(12)会定义名为INT12的结构体,它的位域ad代表12b的有符号补码形式整数,INTBITS(14)会定义名为INT14的结构体,它的位域ad代表14b有符号补码形式整数,其余依次类推,只要AD数据不超过32b,直接使用ad域就行。
2009年10月26日补充:在《AD补码数据和位段结构体(续)》中,使用template而不是Macro来实现代码中的位段结构体。