【问题标题】:Proper way for declaring binary rotation preprocessor macro声明二进制旋转预处理器宏的正确方法
【发布时间】:2019-03-07 14:51:17
【问题描述】:

我想为二进制旋转创建宏。 我的目标是使这些宏对uint32_tuint64_t 操作数类型都通用。

我来到了这个实现:

#define ROTL(X, N)  (((X) << (N)) | ((X) >> (8 * sizeof(X) - (N))))
#define ROTR(X, N)  (((X) >> (N)) | ((X) << (8 * sizeof(X) - (N))))

这些宏工作正常,但gcc 编译器在编译期间会产生警告:

warning: right shift count >= width of type [enabled by default]
#define ROTL(X, N)  (((X) << (N)) | ((X) >> (8 * sizeof(X) - (N))))

warning: left shift count >= width of type [enabled by default]
#define ROTL(X, N)  (((X) << (N)) | ((X) >> (8 * sizeof(X) - (N))))

我知道编译器抱怨XN 的类型可能不匹配。但是即使我同时投射XN 也会产生警告:

ROTL((uint32_t)0xdeadbeef, (uint32_t)0U);

我该怎么做才能以正确的方式消除这些警告?

【问题讨论】:

  • 这在生成的汇编代码方面效率不高。如果您需要速度,您可能想要使用编译器内在函数(很大程度上依赖于平台/编译器)。
  • 由于这些宏对每个参数进行两次评估,请注意不要使用具有副作用的参数调用它们。
  • @Jabberwocky:实际上,现在几乎所有的编译器都足够聪明,可以理解宏发生了什么,并用适当的内在函数替换它(参见godbolt.org/z/R7WQgM)。你可以使用 gcc、clang、mcvc 来获得它。
  • 您需要显示实际的宏调用,以了解编译器为何向您发出警告。

标签: c gcc macros binary bit-manipulation


【解决方案1】:

当第二个参数为零或大于第一个参数的位数时,您会遇到一些问题。您可以尝试使用该模块来摆脱这个问题。通过这个简单的示例,这似乎可行:

#include <stdint.h>
#include <inttypes.h>
#include <stdio.h>
#include <limits.h>

#define ROTL(X, N) \
    (((X) << ((N) % (CHAR_BIT * sizeof(X)))) | \
     ((X) >> ((CHAR_BIT * sizeof(X) - (N)) % (CHAR_BIT * sizeof(X)))))

int main() {
    for (int i = 0; i < 100; i++) {
        uint32_t a = ROTL(UINT32_C(0xdeadbeef), i);
        printf("%d\t%"PRIX32"\n", i, a);
    }
    return 0;
}

我使用了来自limits.hCHAR_BIT 和来自inttypes.hUINT32_CPRIX32。您可以自行调整ROTR 来做同样的事情。

【讨论】:

  • 如果 N 为负数,您可能会偏移负数。把它修好很难。使用&amp; 0x1F 之类的位掩码代替模数可能会起作用,但必须为每种类型正确选择掩码。
  • 掩码应该可以工作,但在技术上对于有符号类型和负值来说是 UB。或者将负旋转视为另一种旋转。当旋转因子为常数时,编译器应将其全部优化掉。
  • @JonathanLeffler 关于Fixing that well is hard。我建议CHAR_BIT --> (uintmax_t)CHAR_BIT 确保未签名的% 操作。
【解决方案2】:

仅当第二个参数为零时才会收到警告。所以,如果N == 0:

什么都不做
#define ROTL(X, N)  ((N) == 0 ? (X) : (((X) << (N)) | ((X) >> (8 * sizeof(X) - (N)))))

【讨论】:

  • 这如何回答这个问题?
  • 这个版本的宏响应What can I do to get rid of these warning the proper way?,既适用于uint32_t,也适用于uint64_t
  • 他只有在第二个参数为零时才会收到警告。所以以一种特殊的方式处理它,实际上是解决问题的方法。
  • @Giovanni 它不处理这种情况:ROTL(x, 32)
  • 嗯,其实第二个参数必须在[0, (sizeof(X) * 8 - 1)],否则算法根本不起作用。
【解决方案3】:

将移位的操作数转换为最大可能宽度。

#define ROTL(X, N)  (((uint64_t)(X) << (N)) | ((uint64_t)(X) >> (8 * sizeof(X) - (N))))

【讨论】:

  • 此解决方案可能适用于 uint32_t 作为输入,但并不通用,因为在使用 uint64_t 而不是 uint32_t 时会产生相同的警告。
猜你喜欢
  • 2015-10-09
  • 1970-01-01
  • 2019-05-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-14
  • 2011-04-08
  • 2011-02-18
相关资源
最近更新 更多