C语言 在一个无符号整型中存储2个有符号短整型

h7wcgrx3  于 2022-12-11  发布在  其他
关注(0)|答案(5)|浏览(167)

这是给定的:

signed short a, b;
a = -16;
b = 340;

现在我想把这两个有符号的短取存储在一个无符号的int中,以后再检索这两个有符号的短取。我试过了,但是得到的短取是不一样的:

unsigned int c = a << 16 | b;

signed short ar, br;
ar = c >> 16;
br = c & 0xFFFF;
u4dcyp6a

u4dcyp6a1#

楼主差点就猜对了

#include <assert.h>
#include <limits.h>

unsigned ab_to_c(signed short a, signed short b) {
  assert(SHRT_MAX == 32767);
  assert(UINT_MAX == 4294967295);
  // unsigned int c = a << 16 | b; fails as `b` get sign extended before the `|`.
  // *1u insures the shift of `a` is done as `unsigned` to avoid UB 
  //    of shifting into the sign bit.
  unsigned c = (a*1u << 16) | (b & 0xFFFF);
  return c;
}

void c_to_ab(unsigned c, signed short *a, signed short *b) {
  *a = c >> 16;
  *b = c & 0xFFFF;
}
q5lcpyga

q5lcpyga2#

由于a具有负值,

unsigned int c = a << 16 | b;

导致未定义的行为。
来自C99标准(重点矿井):

6.5.7按位移位运算符

4 E1 << E2的结果是E1左移E2位位置;空出的比特会以零填满。如果E1是不带负数号的型别,则结果的值为E1 x 2E2,以结果型别中可表示的最大值再加1为模约简。如果E1是带负数号的型别,且值为非负数,且E1 x 2E2可在结果型别中表示,那么这就是结果值;否则,行为未定义。
您可以将signed short明确转换为unsigned short,以取得可预测的行为。

#include <stdio.h>

int main()
{
   signed short a, b;
   a = -16;
   b = 340;

   unsigned int c = (unsigned short)a << 16 | (unsigned short)b;

   signed short ar, br;
   ar = c >> 16;
   br = c & 0xFFFF;

   printf("ar: %hd, br: %hd\n", ar, br);
}

输出量:

ar: -16, br: 340
nbewdwxp

nbewdwxp3#

这真的很奇怪,我已经编译了你的代码,它对我来说很有效,也许这是一个未定义的行为,我不确定,但是如果我是你,我会添加强制转换,以明确避免一些位丢失,这可能是或可能不是由滥用二进制补码或编译器自动强制转换引起的....
在我看来,发生的事情可能是你把所有的位移出一个...试试这个

unsigned int c = ((unsigned int) a) << 16 | b;
jk9hmnmh

jk9hmnmh4#

这是因为你使用了一个无符号整型数,它通常是32位,而一个带负数号的短整型数通常是16位。当你把一个带负数的短整型数放入一个无符号整型数中时,这个“负”位会被解释为正数的一部分。所以你在无符号整型数中得到的是一个非常不同的数字。
存储两个正数可以解决这个问题....但您可能需要存储一个负数。

dced5bon

dced5bon5#

不确定这种方式是否有利于便携性或其他,但我使用...

#ifndef STDIO_H
#define STDIO_H
#include <stdio.h>
#endif

#ifndef SDTINT_H
#define STDINT_H
#include <stdint.h>
#endif

#ifndef BOOLEAN_TE
#define BOOLEAN_TE
typedef enum {false, true} bool;
#endif  

#ifndef UINT32_WIDTH
#define UINT32_WIDTH 32 // defined in stdint.h, inttypes.h even in libc.h... undefined ??
#endif

typedef struct{
struct{                 // anonymous struct
    uint32_t    x;
    uint32_t    y;
};}ts_point;

typedef struct{
struct{                 // anonymous struct
    uint32_t    line;
    uint32_t    column;
};}ts_position;

bool    is_little_endian()
{
    uint8_t n = 1;
    return *(char *)&n == 1;
}

int main(void)
{
    uint32_t    x, y;
    uint64_t    packed;
    ts_point    *point;
    ts_position *position;

    x = -12;
    y = 3457254;
    printf("at start: x = %i | y = %i\n", x, y);
    if (is_little_endian()){
        packed = (uint64_t)y << UINT32_WIDTH | (uint64_t)x;
    }else{
        packed = (uint64_t)x << UINT32_WIDTH | (uint64_t)y;
    }
    printf("packed: position = %llu\n", packed);
    point = (ts_point*)&packed;
    printf("unpacked: x = %i | y = %i\n", point->x, point->y); // access via pointer
    position = (ts_position*)&packed;
    printf("unpacked: line = %i | column = %i\n", position->line, position->column);
    return 0;
}

我喜欢我做的方式,因为它提供了很多准备,可以在许多方面应用,即02x32,04x16,08x08等。我是新的C,所以请随时批评我的代码和方式做...谢谢

相关问题