【问题标题】:Why use the Bitwise-Shift operator for values in a C enum definition?为什么对 C 枚举定义中的值使用按位移位运算符?
【发布时间】:2010-10-22 18:44:03
【问题描述】:

Apple 有时会在其 enum 定义中使用 Bitwise-Shift 运算符。例如,在 Core Graphics 的 CGDirectDisplay.h 文件中:

enum {
  kCGDisplayBeginConfigurationFlag  = (1 << 0),
  kCGDisplayMovedFlag           = (1 << 1),
  kCGDisplaySetMainFlag         = (1 << 2),
  kCGDisplaySetModeFlag         = (1 << 3),
  kCGDisplayAddFlag         = (1 << 4),
  kCGDisplayRemoveFlag          = (1 << 5),
  kCGDisplayEnabledFlag         = (1 << 8),
  kCGDisplayDisabledFlag        = (1 << 9),
  kCGDisplayMirrorFlag          = (1 << 10),
  kCGDisplayUnMirrorFlag        = (1 << 11),
  kCGDisplayDesktopShapeChangedFlag = (1 << 12)
};
typedef uint32_t CGDisplayChangeSummaryFlags;

为什么不简单地在“普通”枚举中使用递增的int

【问题讨论】:

  • 这很容易看出枚举都是只设置了一个位的标志。
  • 实际上 Loki 指出的真正原因是为了可读性。你可以使用 1, 2, 4, 8, 16 等等(2 的幂),但是你冒着犯错的风险,而 1

标签: c enums bitwise-operators bit-shift


【解决方案1】:

也许用十六进制(或二进制)写值会有所帮助:-)

enum {
  kCGDisplayBeginConfigurationFlag  = (1 << 0), /* 0b0000000000000001 */
  kCGDisplayMovedFlag               = (1 << 1), /* 0b0000000000000010 */
  kCGDisplaySetMainFlag             = (1 << 2), /* 0b0000000000000100 */
  kCGDisplaySetModeFlag             = (1 << 3), /* 0b0000000000001000 */
  kCGDisplayAddFlag                 = (1 << 4), /* 0b0000000000010000 */
  kCGDisplayRemoveFlag              = (1 << 5), /* 0b0000000000100000 */
  kCGDisplayEnabledFlag             = (1 << 8), /* 0b0000000100000000 */
  kCGDisplayDisabledFlag            = (1 << 9), /* 0b0000001000000000 */
  kCGDisplayMirrorFlag              = (1 << 10),/* 0b0000010000000000 */
  kCGDisplayUnMirrorFlag            = (1 << 11),/* 0b0000100000000000 */
  kCGDisplayDesktopShapeChangedFlag = (1 << 12) /* 0b0001000000000000 */
};

现在您可以添加它们(或“或”它们)并获得不同的值

kCGDisplayAddFlag | kCGDisplayDisabledFlag /* 0b0000001000010000 */

【讨论】:

  • 很清楚,您可以考虑将其设置为接受的答案。
  • 这样做有什么限制?在用尽可能性之前,您最多可以将 16 个值放入此枚举中吗?
  • @mylogon:它依赖于实现。 C11保证unsigned int可以保持16位,unsigned long至少可以保持32位。
【解决方案2】:

通过这种方式,您可以将多个标志添加在一起以创建一个“一组”标志,然后可以使用&amp; 找出任何给定标志是否在这样的一组中。

如果它只是使用递增的数字,你就无法做到这一点。

例子:

int flags = kCGDisplayMovedFlag | kCGDisplaySetMainFlag; // 6
if(flags & kCGDisplayMovedFlag) {} // true
if(flags & kCGDisplaySetModeFlag) {} // not true

【讨论】:

  • 嗯,这解释了我正在帮助解开的一些 C++ 代码的一些事情。谢谢!
【解决方案3】:

C# 7 中的新功能终于添加了二进制字面量,所以你可以这样写:

enum MyEnum
{
    kCGDisplayBeginConfigurationFlag  = 0b0000000000000001;
    kCGDisplayMovedFlag               = 0b0000000000000010;
    kCGDisplaySetMainFlag             = 0b0000000000000100;
    kCGDisplaySetModeFlag             = 0b0000000000001000;
    kCGDisplayAddFlag                 = 0b0000000000010000;
    kCGDisplayRemoveFlag              = 0b0000000000100000;
    kCGDisplayEnabledFlag             = 0b0000000001000000;
    kCGDisplayDisabledFlag            = 0b0000000010000000;
    kCGDisplayMirrorFlag              = 0b0000000100000000;
    kCGDisplayUnMirrorFlag            = 0b0000001000000000;
    kCGDisplayDesktopShapeChangedFlag = 0b0000010000000000;
};

如果你想让事情变得更整洁,你可以使用这个:_,这也是 C# 7 的新内容,它允许你在数字中添加空格以使内容更具可读性,如下所示:

enum MyEnum
{
    kCGDisplayBeginConfigurationFlag  = 0b_0000_0000_0000_0001;
    kCGDisplayMovedFlag               = 0b_0000_0000_0000_0010;
    kCGDisplaySetMainFlag             = 0b_0000_0000_0000_0100;
    kCGDisplaySetModeFlag             = 0b_0000_0000_0000_1000;
    kCGDisplayAddFlag                 = 0b_0000_0000_0001_0000;
    kCGDisplayRemoveFlag              = 0b_0000_0000_0010_0000;
    kCGDisplayEnabledFlag             = 0b_0000_0000_0100_0000;
    kCGDisplayDisabledFlag            = 0b_0000_0000_1000_0000;
    kCGDisplayMirrorFlag              = 0b_0000_0001_0000_0000;
    kCGDisplayUnMirrorFlag            = 0b_0000_0010_0000_0000;
    kCGDisplayDesktopShapeChangedFlag = 0b_0000_0100_0000_0000;
};

使跟踪数字变得更加容易。

【讨论】:

  • 我希望确保我想做的事情在 C 语言中是合法的。这个线程专门针对 C 中的枚举和位运算符。似乎还有很多其他关于枚举和位的线程专门针对 C# 的操作。我不确定将有关 C# 的详细信息添加到 this 线程是否有帮助。
【解决方案4】:

如果您有 FlagA=1、FlagB=2 和 FlagC=3,则 FlagA 或 FlagB 将给出与 FlagC 相同的值。移位运算符用于确保标志的每个组合都是唯一的。

【讨论】:

    【解决方案5】:

    这将允许一个变量轻松组合多个标志:

    unit32_t multFlag = kCGDisplayRemoveFlag | kCGDisplayMirrorFlag | kCGDisplaySetMainFlag'
    

    【讨论】:

      【解决方案6】:

      让我给你一个更实际的例子。在 c++ 中,当你想打开一个文件(打开输出,二进制模式与文本模式相反)时,你可以这样做:

      const char *filename = "/home/xy/test.bin";
      fstream output(filename, ios::out | ios::binary);
      

      您可以看到,ios::out | ios::binary 可以设置两种模式(输出打开和二进制模式)。

      这是如何工作的?它是通过枚举(按位移位值):

      enum _Ios_Openmode 
      { 
        _S_app        = 1L << 0,
        _S_ate        = 1L << 1,
        _S_bin        = 1L << 2,  /* 0b0000000000000100 */
        _S_in         = 1L << 3,
        _S_out        = 1L << 4,  /* 0b0000000000010000 */
        _S_trunc      = 1L << 5
        //.....
      };
      
      /// Perform input and output in binary mode (as opposed to text mode).
      static const openmode binary =  _S_bin;
      
      /// Open for input.  Default for @c ifstream and fstream.
      static const openmode in =      _S_in;
      
      /// Open for output.  Default for @c ofstream and fstream.
      static const openmode out =     _S_out;
      

      如果在enum _Ios_Openmode 中使用值递增1,则必须将set(ios::out)set(ios::binary) 执行两次。一次检查和设置值可能不太方便。

      【讨论】:

        【解决方案7】:

        .. 因为1&lt;&lt;7 看起来比01000000 更简洁易读。不是吗?

        【讨论】:

        • 这不能回答问题
        【解决方案8】:

        使用#define 更容易理解。但是枚举可以将这些值分组。

        【讨论】:

        • 这根本没有回答问题。
        猜你喜欢
        • 1970-01-01
        • 2022-03-30
        • 2011-01-10
        • 2013-09-21
        • 2021-02-06
        • 1970-01-01
        • 1970-01-01
        • 2015-10-24
        相关资源
        最近更新 更多