C语言 - 使用位移运算符进行进制转换

5
我正在尝试在C语言中将一些数据从十六进制转换为Base64。我在网上找到了一个算法,但我真的很想知道它是如何工作的,而不仅仅是实现它并启动它。如果有人能解释一下以下内容的工作原理,我会非常感激。我一直在阅读关于移位运算符的文章,但似乎并没有像我想象的那样理解它们...它还没有完全适应我。
for (x = 0; x < dataLength; x += 3) 
{
  /* these three 8-bit (ASCII) characters become one 24-bit number */
  n = data[x] << 16;

  if((x+1) < dataLength)
     n += data[x+1] << 8;

  if((x+2) < dataLength)
     n += data[x+2];

  /* this 24-bit number gets separated into four 6-bit numbers */
  n0 = (uint8_t)(n >> 18) & 63;
  n1 = (uint8_t)(n >> 12) & 63;
  n2 = (uint8_t)(n >> 6) & 63;
  n3 = (uint8_t)n & 63;

这段代码来自Wikibooks,不是我写的,我只是想理解位移操作以及它如何帮助我转换数据。
非常感谢您的帮助,我真的很感激。
来源:Base64

1
如果你理解位运算符的作用,那么在这段代码中你还有什么不理解的呢? - user529758
您可以在问题中加入对 >><<& 的理解,然后再看看您是否还有疑问。 - user3458
63 = 0b00111111,再加上一些理解,@Arkadiy 建议你自己尝试一下。(0b00111111 是在 RAM 中一个字节内存的二进制表示形式,0b 表示这个数字是二进制的,00111111 是这个二进制数) - tesseract
位移操作的作用就是其名称所示。n >> 1 表示将 n 的最低二进制位舍弃,接下来的二进制位变成新的最低位,以此类推。例如,00011001 >> 1 的结果为 00001100。对于有符号整数类型,还有更多细节(符号位会被传递),但本质上是进行位移操作。通过位移和位掩码(& 运算符)的组合,可以从一个字中间取出一堆位,并将它们视为一个单独的数字。 - user3458
@Arkadiy 符号不一定会在有符号整数中传播。这是实现定义的。 - Filipe Gonçalves
显示剩余2条评论
2个回答

5
首先,输入数据并不像您所说的那样是十六进制。它只是以字节形式存储的数据。该代码将为您提供其base64表示(尽管您发布的代码缺少将n0、n1、n2、n3映射到可打印ASCII字符的部分)。
假设输入的前三个字节为(以二进制表示,每个字母代表一个0或1):
abcdefgh, ijklmnop, qrstuvwx

代码的第一部分将它们组合成一个24位的数字。这是通过将第一个数字向左移动16位,第二个数字向左移动8位并相加来完成的:

  abcdefgh0000000000000000      (abcdefgh << 16)
+ 00000000ijklmnop00000000      (ijklmnop << 8)
  0000000000000000qrstuvwx
  ------------------------
  abcdefghijklmnopqrstuvwx

然后,它通过移位和按位与操作将其分成四个6位数字。例如,第二个数字是通过向右移动12位并与111111进行按位与运算计算得出的。

n     =   abcdefghijklmnopqrstuvwx

n>>12 =   000000000000abcdefghijkl
63    =   000000000000000000111111

And'ing gives:
          000000000000000000ghijkl

哇,现在我真的明白了很多。非常感谢!我没有想到会变得那么清晰。再次感谢! - bry6891

2

好的,这里有一点解释...

data[x]是一个字符数组,一个字符通常由8位组成.. (随机的8位数 01010101) n是一个32位数,这里是一个随机的32位数(01011111000011110000111100001111),想象一下有32位 :)

请记住,n是32位的,而data只有8位.. 让我们走过第一行

 n = data[x] << 16;

<<16的优先级高于等号,因此首先进行计算。

data[x] << 16表示将data[x]在内存中所代表的位向左移动16位。 假设data[x] = 'a',在内存中表示为01100001(1字节),因此让它向左移动16位。

n = 00000000 01100001 00000000 00000000

下面我们有:
if((x+1) < dataLength)
 n += data[x+1] << 8;

这句话的意思是将下一个字符数据[x+1]向左移8位并加到n上;因此我们先将它向左移动8位。

( I assumed it was 'a' again)
00000000 00000000 01100001 00000000 
(this is done in some register in your processor)

现在让我们将它添加到n中。
00000000 01100001 01100001 00000000

next part is

 if((x+2) < dataLength)
 n += data[x+2];

在这里我们做同样的事情,注意没有位移操作,因为n的最后8位是空闲的!我们只需要将它加到n上即可。

b = 01100010(假设 data[x+2] = 'b'),将其加到n上。

  00000000 01100001 01100001 01100010

很好,现在我们有一个24位的数字(实际上n是32位,但我们只需要最后的24位)

接下来的部分

n0 = (uint8_t)(n >> 18) & 63; 
(take note n0 is only 8bits wide or a single unsigned byte)

take n and move it to the left by 18bits and "and" it with 63

n = 00000000 01100001 01100001 01100010
n moved 18bits to right is  00000000 00000000 00000000 00011000

now n is cast to an unsigned int of 8bits (uint8_t)

so now it becomes 00011000

last part is the & operator(bitwise and) 

    00011000 & 
    00111111
n0= 00011000 

现在将此操作重复到其余部分。

哇,非常详尽,太棒了!我希望我能选择两个答案来标记为绿色勾号。感谢您花时间编写这篇文章,它对我很有帮助。 - bry6891

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接