是否可以对多个连续的数组元素进行按位运算?

时间:2018-09-22 17:20:08

标签: c bit-shift

我正在研究几年前编码的AES的旧实现,我想修改ShiftRows函数,该效率非常低。

暂时,我的ShiftRows基本上只交换连续数组元素(用一个字节表示)的值n次以实现循环排列。

我想知道是否有可能将我的element数组转换为单个变量以使用位移运算符进行置换? 这些行是4个无符号字符,因此每个4个字节。

在下面的代码中,似乎只有第一个字节(对应于“ a”)受位移位的影响。

char array[4][4] = {"abcd", "efgh", "ijkl", "mnop"};

int32_t somevar;

somevar = (int32_t)*array[0] >> 16;

自从我不练习C以来已经有很长时间了,所以我可能正在犯一些愚蠢的错误。

1 个答案:

答案 0 :(得分:3)

首先,如果您的主要目标是快速AES实现,而不是实践C或快速但可移植的AES实现(即,可移植性是主要的,效率是次要的),那么您需要编写汇编语言语言(不是C语言),或至少对特定目标使用编译器功能,以便您编写准汇编代码。例如,Intel processors have AES-assist instructions, and GCC has built-in functions for them

第二,如果要使用C语言执行此操作,则理想的主要工作是将所需的操作清楚地表达给编译器。通过这个,我的意思是您希望操作对编译器是透明的,以便其优化程序可以正常工作。使用各种技术重新解释数据(例如,从charint)可能会阻碍编译器的优化能力。 (或者它们可能不,取决于编译器的质量和您编写的特定代码。)

如果您的目标是可移植代码,则可能最好只编写所需的字符运动(只需编写可移动数组元素的简单赋值语句)。好的编译器可以有效地转换它们,甚至在硬件支持的情况下,甚至可以将多个字节移动操作组合为单个字移动操作。

在编写“ fancy”代码以尝试进行优化时,重要的是要了解标准C的规则,正在使用的编译器的属性以及目标硬件。

例如,您有char array[4][4]。这声明了没有特定对齐方式的数组。编译器可能将此数组以任何对齐方式放置在任何位置,例如,不一定要对齐四个字节的倍数。如果然后使用指向此数组第一行的指针并将其转换为指向int的指针,则在某些处理器上加载int的指令可能会失败,因为它们需要{{1} }对象要对齐为四个字节的倍数。在其他处理器上,负载可能会起作用,但比对齐的负载要慢。

一个解决方案是不声明裸数组也不转换指针。相反,您将声明一个并集,其中一个成员可能是四个int的数组,另一个可能是四个四个uint32_t的数组的数组。联合中uint8_t数组的存在将迫使编译器使其适合于硬件。此外,C中允许通过联合来重新解释数据,而通过转换后的指针重新解释数据不是正确的C代码。 (即使满足对齐要求,通过指针重新解释数据也通常会违反别名规则。)

另一方面,在处理位时,通常最好使用无符号类型,就像在加密代码中一样。最好使用uint32_tchar,而不是int32_tuint8_t

关于您的特定代码:

uint32_t

somevar = (int32_t)*array[0] >> 16; array[0]的第一行。根据C的规则,它会自动转换为指向其第一个元素的指针,因此它变为array。然后&array[0][0]*array[0],它是*&array[0][0],它是数组第一行中的第一个array[0][0]。因此,到目前为止的表达式只是第一个char的值。然后,强制转换char将表达式的类型转换为(int32_t)。这不会更改值,因此结果只是第一行中第一个int32_t的值。

您可能会想到的是char* (uint32_t *) &array[0]。它们采用第一行的地址(前一个表达式)或第一行的第一个元素的地址(后一个表达式)(表示相同的位置,但类型不同)并将其转换为指向{ {1}}。然后* (uint32_t) array[0]打算在该地址获取uint32_t。这违反了C规则,应该避免。

您可以使用:

*

然后,您可以使用uint32_t访问单个字节,或者使用union { uint32_t words[4]; uint8_t bytes[4][4]; } block; 访问四个字节的单词。这是否一个好主意取决于上下文和目标。