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

mas*_*eyc 20 c bit-manipulation 32bit-64bit

我最近选择了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;}
Run Code Online (Sandbox Code Playgroud)

其中a是64位,b和c是32位.但是,编译器警告我,我正在将32位变量移位32位.

我的问题是这些:

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

正如我所说的,我对C非常熟悉,但是按位操作符等仍让我头疼.

编辑

我发现我的组合宏实际上并没有组合两个32位变量,而只是简单地将0和0相加,并得到一个结果.
所以,除了我以前的问题,我仍然没有一种方法将两个32位变量组合起来得到一个64位变量; 关于如何做到这一点的建议将不胜感激.

Sec*_*ure 22

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

ISO/IEC 9899:1999 6.5.7 按位移位算子 3

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

C11又名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) )
Run Code Online (Sandbox Code Playgroud)

"分裂"可能需要额外的强制转换,以消除过度偏执编译器对精确丢失的警告.

#define splitup(a,b,c) ( (b) = (unsigned long)((a) >> 32), (c) = (unsigned long)((a) & 0xffffffff) )
Run Code Online (Sandbox Code Playgroud)

请不要考虑将自编写加密用于生产代码.


AnT*_*AnT 13

在C和C++中,未将32位值移位32位或更多位置.未定义的原因之一是在某些硬件平台上,32位移位指令仅考虑提供的移位计数的5个最低位.这意味着无论你传递的移位数是多少,它都会被解释为模32次.试图在这样的平台上移动32实际上会移0,即根本不移位.

语言作者不希望为这种平台编写的编译器负担,其任务是在进行轮班之前分析班次计数.相反,语言规范说行为是未定义的.这意味着如果您希望从32位移位获得0(或更多)的0值,则由您来识别情况并相应地处理它.

  • 值得指出的是,在这种情况下,"某些平台"包括x86. (7认同)
  • @ShinTakezou:有些程序需要评估`x &lt;&lt; (y &amp; 31)`。有时程序需要 `y &gt;= 32 ?0 : x &lt;&lt; y`。有些人会对这两种结果同样满意。如果标准强制要求任何一种行为,那么至少某些平台的编译器将不得不为不关心他们收到哪个结果的程序生成不必要的代码。更糟糕的是,当需要相反类型结果的程序在“自然”返回程序想要的结果的平台上运行时,程序员将不得不编写代码来克服编译器引入的不需要的额外代码的影响。 (2认同)