【问题标题】:How to circular shift an array of 4 chars?如何循环移位4个字符的数组?
【发布时间】:2011-04-24 12:16:16
【问题描述】:

我有一个由四个无符号字符组成的数组。我想将其视为 32 位数字(假设 char 的高位不关心。我只关心低 8 位)。然后,我想将它循环移动任意数量的位置。我有几个不同的班次大小,都是在编译时确定的。

例如

unsigned char a[4] = {0x81, 0x1, 0x1, 0x2};
circular_left_shift(a, 1);
/* a is now { 0x2, 0x2, 0x2, 0x5 } */

编辑:每个人都想知道为什么我没有提到 CHAR_BIT != 8,因为这是标准 C。我没有指定平台,所以你为什么假设一个?

【问题讨论】:

  • 为什么不将它存储在 32 位数据中,例如 int(取决于机器和所有)?
  • 如果 char 是 16 位,那么您的示例是错误的,基本上您想将它们视为 8 位字符,对吗?

标签: c


【解决方案1】:
static void rotate_left(uint8_t *d, uint8_t *s, uint8_t bits)
{
   const uint8_t octetshifts = bits / 8;
   const uint8_t bitshift = bits % 8;
   const uint8_t bitsleft = (8 - bitshift);
   const uint8_t lm = (1 << bitshift) - 1;
   const uint8_t um = ~lm;
   int i;

   for (i = 0; i < 4; i++)
   {
       d[(i + 4 - octetshifts) % 4] =
           ((s[i] << bitshift) & um) | 
           ((s[(i + 1) % 4] >> bitsleft) & lm);
   }
}   

显然

【讨论】:

  • 这看起来很有希望,让我运行几个测试用例。它比我的第一次尝试干净得多。
  • 我看到你假设了小端,但它可以很容易地修改为大端..
【解决方案2】:

同时牢记纯 C,最好的方法是

inline void circular_left_shift(char *chars, short shift) {
    __int32 *dword = (__int32 *)chars;
    *dword = (*dword << shift) | (*dword >> (32 - shift));
}

嗯,char 是 16 位长,我不清楚。我认为int 仍然是 32 位。

inline void circular_left_shift(char *chars, short shift) {
    int i, part;
    part = chars[0] >> (16 - shift);
    for (i = 0; i < 3; ++i)
        chars[i] = (chars[i] << shift) | (chars[i + 1] >> (16 - shift));
    chars[3] = (chars[3] << shift) | part;
}

或者你可以放松这个循环。

您可以进一步研究 asm 指令ror,在 x86 上,它能够执行最多左移 31 位的这种移位。有点像

MOV CL, 31
ROR EAX, CL

【讨论】:

  • 我会这样做,但 CHAR_BIT 是 16,因此在 unsigned char[4] 顶部别名 32 位字不起作用。我不能依赖非标准 C 功能,但感谢您的回复。
  • 刚刚修复。目标机器是什么?
  • 碰巧是一个 TI DSP,其中 int != 32 位,但无论如何我都没有看到这在您的代码中会很重要。这仅限于班次
【解决方案3】:

使用union:

typedef union chr_int{
   unsigned int  i;
   unsigned char c[4];
};

它更安全(因为指针别名)并且更容易操作。

编辑:您之前应该提到您的 char 不是 8 位。但是,这应该可以解决问题:

#define ORIG_MASK 0x81010102
#define LS_CNT 1

unsigned char a[4] = { 
    ((ORIG_MASK <<  LS_CNT      ) | (ORIG_MASK >> (32 - LS_CNT))) & 0xff,
    ((ORIG_MASK << (LS_CNT +  8)) | (ORIG_MASK >> (24 - LS_CNT))) & 0xff,
    ((ORIG_MASK <<  LS_CNT + 16)) | (ORIG_MASK >> (16 - LS_CNT))) & 0xff,
    ((ORIG_MASK << (LS_CNT + 24)) | (ORIG_MASK >> ( 8 - LS_CNT))) & 0xff
};

【讨论】:

  • +1 用于unsigned int,它实际上适用于问题中的测试数据。这与平台上的字节序无关吗?
  • 查看我对上一个答案的评论。
  • 好吧,如果 char 数组也是编译时间常数,那就没问题了。我以为你问过这个。我现在看到了:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-05-24
  • 1970-01-01
  • 1970-01-01
  • 2018-12-15
  • 2013-09-11
  • 1970-01-01
  • 2011-02-04
相关资源
最近更新 更多