简单的位操作失败

时间:2013-05-17 14:49:12

标签: c bit-manipulation

我正在学习C中的位操作,我编写了一个简单的程序。但程序失败了。有人可以查看这段代码吗? 基本上我想提取并重新组合一个4字节的“长”变量到它的感应字节,反之亦然。这是我的代码:

    printf("sizeof char= %d\n", sizeof(char));
    printf("sizeof unsigned char= %d\n", sizeof(unsigned char));
    printf("sizeof int= %d\n", sizeof(int));
    printf("sizeof long= %d\n", sizeof(long));
    printf("sizeof unsigned long long= %d\n", sizeof(unsigned long long));

    long val = 2;
    int k = 0;
    size_t len = sizeof(val);
    printf("val = %ld\n", val);
    printf("len = %d\n", len);

    char *ptr;
    ptr = (char *)malloc(sizeof(len));
    //converting 'val' to char array
    //val = b3b2b1b0 //where 'b is 1 byte. Since 'long' is made of 4 bytes, and char is 1 byte, extracting byte by byte of long into char
    //do{
        //val++;
    for(k = 0; k<len; k++){
        ptr[k] = ((val >> (k*len)) && 0xFF);
        printf("ptr[%d] = %02X\n", k,ptr[k]);
    }
    //}while(val < 12);

    //reassembling the bytes from char and converting them to long
    long xx = 0;
    int m = 0;
    for(m = 0; m< len; m++){
        xx = xx |(ptr[m]<<(m*8));
    }
    printf("xx= %ld\n", xx);

为什么我看不到xx返回2?此外,无论'val'的值如何,ptr [0]似乎存储1 :( 请帮忙

提前致谢

3 个答案:

答案 0 :(得分:4)

ptr[k] = ((val >> (k*len)) && 0xFF);

应该是

ptr[k] = ((val >> (k*8)) & 0xFF);

&&用于条件语句和&amp;按位和。 此外,当您将值拆分为字符时,您希望使用与字节中一样多的位移位循环的每次迭代。这几乎总是8,但可能是其他东西。头文件limits.h包含有关该信息的信息。

答案 1 :(得分:3)

我注意到的一些事情:

  1. 你正在使用布尔&amp;&amp;运算符而不是bitwise&amp;
  2. 你正在转移&#34; k * len&#34;而不是&#34; k * 8&#34;
  3. 您正在使用&#34; sizeof(len)&#34;分配数组,而不仅仅是&#34; len&#34;
  4. 您正在使用&#34; char&#34;而不是&#34; unsigned char&#34;。这将使&#34;(ptr [m]&lt;&lt;(m * 8))&#34;表达式有时会给你一个负数。
  5. 因此,您的代码的固定版本将是:

    printf("sizeof char= %d\n", sizeof(char));
    printf("sizeof unsigned char= %d\n", sizeof(unsigned char));
    printf("sizeof int= %d\n", sizeof(int));
    printf("sizeof long= %d\n", sizeof(long));
    printf("sizeof unsigned long long= %d\n", sizeof(unsigned long long));
    
    long val = 2;
    int k = 0;
    size_t len = sizeof(val);
    printf("val = %ld\n", val);
    printf("len = %d\n", len);
    
    unsigned char *ptr;
    ptr = (unsigned char *)malloc(len);
    //converting 'val' to char array
    //val = b3b2b1b0 //where 'b is 1 byte. Since 'long' is made of 4 bytes, and char is 1 byte, extracting byte by byte of long into char
    //do{
        //val++;
    for(k = 0; k<len; k++){
        ptr[k] = ((val >> (k*8)) & 0xFF);
        printf("ptr[%d] = %02X\n", k,ptr[k]);
    }
    //}while(val < 12);
    
    //reassembling the bytes from char and converting them to long
    long xx = 0;
    int m = 0;
    for(m = 0; m< len; m++){
        xx = xx |(ptr[m]<< m*8);
    }
    printf("xx= %ld\n", xx);
    

    此外,将来,这样的问题更适合https://codereview.stackexchange.com/

答案 2 :(得分:1)

正如其他人现在提到的那样,我不确定ptr[k] = ((val >> (k*len)) && 0xFF);是否符合您的要求。 &&运算符是一个布尔运算符。如果(value >> (k*len))是某个非零值,并且0xFF是某个非零值,则存储到ptr[k]中的值将为1。这就是布尔运算符的工作方式。也许您打算使用&代替&&

此外,您已选择使用适用于unsigned类型的移位运算符,但对于已签名类型具有各种非可移植方面。 xx = xx |(ptr[m]<<(m*8));可能会调用未定义的行为,例如,因为它看起来可能会导致有符号整数溢出。

在C中,sizeof (char) 总是 1,因为sizeof运算符会告诉您有多少char用于表示类型。例如。 sizeof (int)告诉您有多少char用于表示int。这是CHAR_BIT的变化。因此,您的代码不应该依赖sizeof类型。

事实上,如果您希望代码是可移植的,那么您不应期望能够在int中存储大于32767或小于-32767的值。这与大小无关,因为可能存在填充位。总结一下:sizeof a类型不一定反映它可以存储的值集合!


为其应用选择变量的类型,可移植。如果您的应用程序不需要超出该范围的值,那么int将会正常运行。否则,您可能想要考虑使用long int,它可以便携地存储(包括)-2147483647和2147483647之间的值。如果您需要超出该值的值,请使用long long int,它将为您提供至少包含-9223372036854775807和922337203685477580之间值的保证范围。除此之外的任何内容都可能需要多精度算术库,例如{{3 }}

如果您不希望使用负值,则应使用unsigned类型。

考虑到你的可移植选择整数类型,现在有意义的是你可以设计一种可移植的方式将这些整数写入文件,并从文件中读取这些整数。您需要将符号和绝对值提取到unsigned int

unsigned int sign = val < 0; /* conventionally 1 for negative, 0 for positive */
unsigned int abs_val = val;
if (val < 0) { abs_val = -abs_val; }

...然后构造一个由abs_valsign组成的8位块数组,合并在一起。我们已经决定使用便携式决策,我们的int只能存储16位,因为我们只存储-32767和32767之间的值。结果,不需要循环或按位移位。我们可以使用乘法来移动我们的符号位,并使用除法/模数来减少我们的绝对值。考虑到符号通常与最重要的位一起使用,它位于数组的开始(大端)或结束(小端)。

unsigned char big_endian[] = { sign * 0x80 + abs_val / 0x100,
                               abs_value % 0x100 };
unsigned char lil_endian[] = { abs_value % 0x100,
                               sign * 0x80 + abs_val / 0x100 };

为了扭转这个过程,我们相反地执行相反的操作(即,使用除法和模来代替乘法,乘法代替除法和加法,提取符号位并改变值):< / p>

unsigned int big_endian_sign = array[0] / 0x80;
int big_endian_val = big_endian_sign
                   ? -((array[0] % 0x80) * 0x100 + array[1])
                   :  ((array[0] % 0x80) * 0x100 + array[1]);

unsigned int lil_endian_sign = array[1] / 0x80;
int lil_endian_val = lil_endian_sign
                   ? -((array[1] % 0x80) * 0x100 + array[0])
                   :  ((array[1] % 0x80) * 0x100 + array[0]);

long的代码变得有点复杂,使用二元运算符变得值得。符号和绝对值的提取基本保持不变,唯一的变化是变量的类型。我们仍然不需要循环,因为我们做出了一个决定,我们只关心值可表示的值。以下是我如何从long val转换为unsigned char[4]

unsigned long sign = val < 0; /* conventionally 1 for negative, 0 for positive */
unsigned long abs_val = val;
if (val < 0) { abs_val = -abs_val; }

unsigned char big_endian[] = { (sign << 7) | ((abs_val >> 24) & 0xFF),
                               (abs_val >> 16) & 0xFF,
                               (abs_val >> 8) & 0xFF,
                               abs_val & 0xFF };
unsigned char lil_endian[] = { abs_val & 0xFF,
                               (abs_val >> 8) & 0xFF,
                               (abs_val >> 16) & 0xFF,
                               (sign << 7) | ((abs_val >> 24) & 0xFF) };

...以下是我将如何转换回签名值:

unsigned int big_endian_sign = array[0] >> 7;
long big_endian_val = big_endian_sign
                   ? -((array[0] & 0x7F) << 24) + (array[1] << 16) + (array[2] << 8) + array[3]
                   :  ((array[0] & 0x7F) << 24) + (array[1] << 16) + (array[2] << 8) + array[3];

unsigned int lil_endian_sign = array[3] >> 7;
long lil_endian_val = lil_endian_sign
                   ? -((array[3] & 0x7F) << 24) + (array[2] << 16) + (array[1] << 8) + array[0]
                   :  ((array[3] & 0x7F) << 24) + (array[2] << 16) + (array[1] << 8) + array[0];

我将让您为unsignedlong long类型设计一个方案......并开放发言以征求意见: