C可变宽度位域

时间:2014-04-18 19:09:25

标签: c gcc struct

我不会在这里提出很多问题,所以请原谅我,如果我的问题需要技巧和#34;有点生疏了。这是:


我环顾四周,似乎无法找到解决方法。

这个想法是程序打印出一个具有任意位大小的数字的每个可能值的十进制(有符号和无符号),十六进制和二进制表示。它接受来自argv的位大小,并且示例输出应该是(关注二进制表示):

$ ./test 2
00
01
10
11
$ ./test 4
0000
0001
0010
0011
0100
0101
0110
0111
1000
1001
1010
1011
1100
1101
1110
1111

如果我使用预处理器宏,这可以正常工作,但这意味着我每次想要更改位大小时都必须重新编译。如果不是一个小小的警告,Argv会解决我所有的问题。这就是编译器不喜欢我想做的事情,即使它看起来很合乎逻辑。

我想做的是:

int bit_size = atoi(argv[1]);
struct { unsigned int a : bit_size; } test;

但是编译器给了我一个错误,非常严厉地告诉我我不能这样做(" bitfield不是整数常量")。

好的......所以我尝试使用const int代替:

const int bit_size = atoi(argv[1]);
struct { unsigned int a : bit_size; } test;

我得到了同样的错误。

请注意:GCC 想要我做的是:

struct { unsigned int a : 8; } test;

它没有任何问题。但我需要能够改变它。

我很困惑。

请注意,我不希望或不需要位域宽度来改变节目中期,这就是我假设GCC试图阻止我做的事情。这实际上是一次性操作。

另请注意,我并没有尝试在像this question这样的常规变量上执行此操作(以及许多其他类似的变量)。

This question也与我想要完成的任务无关。


此外,如果它有帮助,这是一个在没有错误的情况下运行时显示的示例(位域宽度= 4):

$ ./test 4
$ cat table.md
Table for a/an 4-bit integer: 

| Unsigned | Signed | Hex | Binary | Sign Bit |
|:--------:|:------:|:---:|:------:|:--------:|
|    0     |   0    |  0  |  0000  |    0     |
|    1     |   1    |  1  |  0001  |    0     |
|    2     |   2    |  2  |  0010  |    0     |
|    3     |   3    |  3  |  0011  |    0     |
|    4     |   4    |  4  |  0100  |    0     |
|    5     |   5    |  5  |  0101  |    0     |
|    6     |   6    |  6  |  0110  |    0     |
|    7     |   7    |  7  |  0111  |    0     |
|    8     |   -8   |  8  |  1000  |    1     |
|    9     |   -7   |  9  |  1001  |    1     |
|    10    |   -6   |  A  |  1010  |    1     |
|    11    |   -5   |  B  |  1011  |    1     |
|    12    |   -4   |  C  |  1100  |    1     |
|    13    |   -3   |  D  |  1101  |    1     |
|    14    |   -2   |  E  |  1110  |    1     |
|    15    |   -1   |  F  |  1111  |    1     |

3 个答案:

答案 0 :(得分:5)

您无法在C中的运行时定义位字段的大小。但是您不需要位字段来打印二进制值,只需编写一个函数来打印二进制格式的数字,像这里的一个: Is there a printf converter to print in binary format?

然后写一个简单的循环来打印你的数字:

//get n
for (int i = 0; i < n; i++) {
    print_binary(i);
}

编辑: 要回答有关打印以二进制补码编码的负数的问题,这些数字在C中不具有本机类型(如int8_t,int16_t,int32_t ..),就像您发现在N位上编码的带符号字一样在2的补码中,对于负数,你可以使用相等:

Xnegative = 2 ^ N - Xpositive

//get n
for (uint32_t Xpos = 0; Xpos < (1<<n); Xpos++) {
    if (Xpos > 1<<(n-1))
        printf("%d\n", -(1 << n) + Xpos);
    else
        printf("%u\n", Xpos);
}

答案 1 :(得分:1)

所以我想通了。

正如其他答案中所述,GCC字面上不允许您在运行时设置位域的宽度,因为它是在编译期间完成的。

所以我开始考虑为什么我首先需要位域,这样我才能显示负数。

然后我研究了几个签名的整数,看看我是否能找到数学模式。

我找到了这样的模式,并决定不再需要位域。

1010 = 0 - (sign_bit (1000) - raw_number (0010)) = -6

虽然我最终意外地绊倒了另一个算法,但是在调试原始算法的同时也是如此:

number - ( (number << 1) & (1 << number of bits) )

我的笔记让我能理解并确保它不是侥幸:

For a negative number:

n = 10 (binary 1010)
num_bits = 4
power = (1010 << 1 => 10100) & (1 << num_bits => 10000)
  10100
& 10000
  10000
return n (10) - power (16) => -6

For a positive number:

n = 6 (binary 0110)
num_bits = 4
power = (0110 << 1 => 01100) & (1 << num_bits => 10000)
  01100
& 10000
  00000
return n (6) - power (0) => 6

最后的功能:

signed int get_signed(long long int n, int num_bits){
  int power = (n << 1) & (1 << num_bits);
  return n - power;
}

我测试了它,num_bits设置为1,2,4,8和16,并且看,它完美无缺


编辑:

我刚刚使用原始算法

signed int get_tc(long long int n, int num_bits){
  int sign_bit = n >> (num_bits - 1) & 1;
  sign_bit <<= (num_bits - 1);
  int raw = n ^ sign_bit;
  return -1 * (sign_bit - raw);
}

逻辑:

n = 10
num_bits = 4
sign_bit = 10 >> (4-1) & 1
  1010
>>3
  0001
& 0001
  0001
sign_bit <<= (4-1)
  0001
<<3
  1000
sign_bit = 8
raw = 10 ^ 8
  1010
^ 1000
  0010
raw = 2
return -1 * (8 - 2)
  8 - 2 = 6
  6 * -1 = -6
return = -6

n = 6
num_bits = 4
sign_bit = 6 >> (4-1) & 1
  0110
>>3
  0000
& 0001
  0000
sign_bit <<= (4-1)
  0000
<<3
  0000
sign_bit = 0
raw = n ^ sign_bit
  0110
^ 0000
  0110
raw = 6
return -1 * (0 - 6)
  0 - 6 = -6
  -6 * -1 = 6

答案 2 :(得分:0)

我知道你已经明白了,但这是我打印这张桌子的解决方案。特别是因为我更喜欢使用更简单的独占或操作。这取决于采用负数表示,如4位情况下的0x1001为-7,并将其符号扩展为有符号长整型然后打印它。

从两个方面开始,你的数字(比如说,-7 in 4 bits,0b1001),以及一个掩码,其中包含有问题的位数(比方说4)设置为1(0b1111)xor'ed为-1 in您选择的签名代表。因此,对于32位有符号数,掩码为:

面膜:0b1111 ^ 0b11111111111111111111111111111111 = 0b11111111111111111111111111110000

当您使用您的号码对此掩码进行xor时,您最终会在您选择使用的分辨率中使用该号码的符号扩展版本。

0b1001 ^ 0b11111111111111111111111111110000 = 0b11111111111111111111111111111001

这是带符号的32位表示中的-7,易于打印。 Sign extension是一个简单的概念。

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

void makeStrbits(long n,long b,char *strbits)
{
   long i=0, m=1L<<(b-1);
   while(m){
      strbits[i++]= (n&m)?'1':'0';
      m=m>>1;
   }
}

int main(int argc, char *argv[])
{
   long i,bit=4,mask=0;
   char *strbits = NULL;
   if (argc==2) bit = strtol( argv[1],NULL,10 );

   strbits = calloc(1,bit+1);

   for(i=0;i<bit;i++) mask |= (1L<<i);
   mask ^= -1L;

   printf("Table for a %ld bit integer\n", bit);
   printf("unsgn\t sign\t hex\t sbit\t bits\n" );
   for(i=0; i<pow(2,bit); i++) {
      int sign = ((i&(1L<<(bit-1))) != 0);
      makeStrbits(i,bit,strbits);
      printf("%lu\t %ld\t %0x\t %1d\t %s\n", i, sign?i^mask:i, i, sign, strbits );
   }
   free(strbits);
   return 0;
}
相关问题