【问题标题】:What's bad about shifting a 32-bit variable 32 bits?将 32 位变量移位 32 位有什么不好?
【发布时间】:2011-02-08 13:51:44
【问题描述】:

我最近收到了 Bruce Schneier 的《应用密码学》一书,读起来很不错。我现在了解了书中概述的几种算法的工作原理,我想开始用 C 来实现其中的一些。

许多算法的共同点是将一个 x 位密钥分成几个较小的 y 位密钥。例如,Blowfish 的密钥 X 是 64 位的,但您需要将其分成两个 32 位的一半; Xl 和 Xr。

这就是我卡住的地方。我在 C 方面还算不错,但在按位运算符等方面我不是最强的。

在对 IRC 的一些帮助之后,我设法想出了这两个宏:

#define splitup(a, b, c) {b = a >> 32; c = a & 0xffffffff; }
#define combine(a, b, c) {a = (c << 32) | a;}

其中 a 是 64 位,b 和 c 是 32 位。但是,编译器会警告我将 32 位变量移动 32 位这一事实。

我的问题是:

  • 将 32 位变量移位 32 位有什么不好?我猜它是未定义的,但这些宏似乎确实有效。
  • 另外,你会建议我换一种方式吗?

正如我所说,我对 C 相当熟悉,但按位运算符之类的仍然让我头疼。

编辑

我发现我的 combine 宏实际上并没有组合两个 32 位变量,而是简单地将 0 与 a 进行 OR 运算,结果得到 a。
所以,在我之前的问题之上,我仍然没有一种方法可以将两个 32 位变量组合起来得到一个 64 位变量;我们将不胜感激。

【问题讨论】:

  • +1,很棒的问题,感谢您花时间正确格式化并欢迎使用 Stack Overflow!
  • 顺便说一句,我敢打赌,你的组合宏应该是与 b 而不是 a 的 ORing,对吧?

标签: c bit-manipulation 32bit-64bit


【解决方案1】:

是的,这是未定义的行为。

ISO/IEC 9899:1999 6.5.7 移位运算符 ¶3

对每个操作数执行整数提升。结果的类型是提升的左操作数的类型。如果右操作数的值为负数或大于或等于提升的左操作数的宽度,则行为未定义。

C11 aka ISO/IEC 9899:2011 也这么说。

您应该首先将b 转换为目标整数类型。另一点是您应该在宏参数周​​围加上括号以避免运算符优先级的意外。此外,逗号操作符在这里非常有用,可以避免使用大括号,这样宏就可以作为普通命令使用,以分号结束。

#define splitup(a,b,c) ( (b) = (a) >> 32, (c) = (a) & 0xffffffff )
#define combine(a,b,c) ( (a) = ((unsigned long long)(b) << 32) | (c) )

对于 `splitup 以消除过度偏执的编译器对精度损失的警告可能需要额外的强制转换。

#define splitup(a,b,c) ( (b) = (unsigned long)((a) >> 32), (c) = (unsigned long)((a) & 0xffffffff) )

请不要考虑将您自己编写的加密用于生产代码。

【讨论】:

    【解决方案2】:

    在 C 和 C++ 中未定义将 32 位值移位 32 位或更多位。它未定义的原因之一是在某些硬件平台上,32 位移位指令仅考虑提供的移位计数的 5 个最低位。这意味着无论您通过什么移位计数,它将被解释为模 32。尝试在此类平台上移位 32 实际上会移位 0,即根本不移位。

    语言作者不想让为此类平台编写的编译器负担在执行移位之前分析移位计数的任务。相反,语言规范说行为是未定义的。这意味着,如果您想从 32 位移位 32(或更多)中获得 0 值,则由您来识别情况并进行相应处理。

    【讨论】:

    • 可能值得指出的是,在这种情况下,“某些平台”包括 x86。
    • 难以置信...编译器应该“补偿”底层硬件架构的“限制”...如果存在一个根本不实现位移的处理器(奇怪!!)怎么办? >> 或
    • @ShinTakezou:有些程序需要评估x &lt;&lt; (y &amp; 31)。有时程序需要y &gt;= 32 ? 0 : x &lt;&lt; y。有些人对这两种结果都同样满意。如果标准规定了任何一种行为,那么至少某些平台的编译器将不得不为不关心他们收到的结果的程序生成不必要的代码。更糟糕的是,当需要相反结果的程序在“自然”返回程序想要的结果的平台上运行时,程序员必须编写代码来克服编译器引入的不需要的额外代码的影响。
    • @ShinTakezou:C 编译器确实必须对各种硬件特性进行大量补偿。 C 语言规范中有很多地方在语言要求和典型硬件行为之间引入了差距。由于语言的性质(例如,它不是 Java),需要以性能为导向的妥协。显然,作者决定在这种情况下,它应该更接近硬件方面。我不知道支持这一决定的确切论据,但可以肯定地说,位移是一种低级操作,可以从最佳性能中受益。
    • 五年后,有两个答案……很好!我认为,如果您要在特定的不常见硬件/平台上做“繁重”的工作,那么更安全的方法是坚持汇编(可能使用内联汇编函数或类似函数),如果可以的话(我相信你通常可以……),因此您可以确定自己在做什么——前提是您对该 CPU 有足够的了解……否则回到纯 C 并睁大眼睛……
    【解决方案3】:

    将 32 位变量移位 32 位有什么不好?

    0 分配给 n 位整数比将其移动 n 位要好。

    例子:

     0 0 1 0 1 ----- 5 bit Integer  
     0 1 0 1 0 ----- 1st shift  
     1 0 1 0 0 ----- 2nd shift  
     0 1 0 0 0 ----- 3rd shift  
     1 0 0 0 0 ----- 4th shift  
     0 0 0 0 0 ----- 5th shift (all the bits are shifted!)  
    

    我仍然没有将两个 32 位变量组合成 64 位变量的方法

    考虑:a 是 64 位,bc 是 32 位

    a = b;
    a = a << 32; //Note: a is 64 bit
    a = a | c;
    

    【讨论】:

    • 哦,好的。所以我的 combine 宏是 ORing 0 by a 得到 a;如果我是正确的,这意味着它实际上并没有将两个 32 位变量组合成一个 64 位变量。该死。
    【解决方案4】:

    除非这是一些“重新发明轮子以了解其工作原理”的项目,否则不要实现您自己的加密函数。

    曾经。

    使用可用的算法来工作(并选择正确的算法)已经够难的了,不要在生产环境中投入一些自制的加密 API 来自取其辱。机会是your encryption won't encrypt

    【讨论】:

    • 感谢您的提示,但这“重新发明轮子以了解其工作原理”项目之一。 :)
    • 好吧,但是完成后别忘了销毁代码:D
    【解决方案5】:

    将 32 位变量移位 32 位有什么不好?

    除了已经说过的,第 32 位位是符号位,你可能会得到符号扩展来保留 sing,从而丢失有效位。

    【讨论】:

    • 实际上是第 31 位是符号位(即1u&lt;&lt;31)。但无论如何,这与无符号类型无关。
    猜你喜欢
    • 2013-09-18
    • 2015-07-17
    • 1970-01-01
    • 2012-03-17
    • 1970-01-01
    • 2014-08-13
    • 1970-01-01
    • 1970-01-01
    • 2012-10-26
    相关资源
    最近更新 更多