objective-c - 64 位 NS_OPTIONS 位掩码

标签 objective-c c cocoa enums bitmask

上下文

我正在使用 NS_OPTIONS 宏来创建位掩码。我已经为它分配了一种类型的 NSInteger 并且因为我是在 64 位平台上构建的,所以应该给我总共 63 个“槽”来使用(64 位少一位签名)。

这是枚举:

typedef NS_OPTIONS(NSInteger, LPSVGOPlugin) {
    LPSVGOPluginNone                            = 0,
    LPSVGOPluginCleanupAttrs                    = 1 << 0,
    LPSVGOPluginRemoveDoctype                   = 1 << 1,
    LPSVGOPluginRemoveXMLProcInst               = 1 << 2,
    LPSVGOPluginRemoveComments                  = 1 << 3,
    LPSVGOPluginRemoveMetadata                  = 1 << 4,
    LPSVGOPluginRemoveTitle                     = 1 << 5,
    LPSVGOPluginRemoveDesc                      = 1 << 6,
    LPSVGOPluginRemoveUselessDefs               = 1 << 7,
    LPSVGOPluginRemoveEditorsNSData             = 1 << 8,
    LPSVGOPluginRemoveEmptyAttrs                = 1 << 9,
    LPSVGOPluginRemoveHiddenElems               = 1 << 10,
    LPSVGOPluginRemoveEmptyText                 = 1 << 11,
    LPSVGOPluginRemoveEmptyContainers           = 1 << 12,
    LPSVGOPluginRemoveViewBox                   = 1 << 13,
    LPSVGOPluginCleanupEnableBackground         = 1 << 14,
    LPSVGOPluginMinifyStyles                    = 1 << 15,
    LPSVGOPluginConvertStyleToAttrs             = 1 << 16,
    LPSVGOPluginConvertColors                   = 1 << 17,
    LPSVGOPluginConvertPathData                 = 1 << 18,
    LPSVGOPluginConvertTransform                = 1 << 19,
    LPSVGOPluginRemoveUnknownsAndDefaults       = 1 << 20,
    LPSVGOPluginRemoveNonInheritableGroupAttrs  = 1 << 21,
    LPSVGOPluginRemoveUselessStrokeAndFill      = 1 << 22,
    LPSVGOPluginRemoveUnusedNS                  = 1 << 23,
    LPSVGOPluginCleanupIDs                      = 1 << 24,
    LPSVGOPluginCleanupNumericValues            = 1 << 25,
    LPSVGOPluginMoveElemsAttrsToGroup           = 1 << 26,
    LPSVGOPluginMoveGroupAttrsToElems           = 1 << 27,
    LPSVGOPluginCollapseGroups                  = 1 << 28,
    LPSVGOPluginRemoveRasterImages              = 1 << 29,
    LPSVGOPluginMergePaths                      = 1 << 30,
    LPSVGOPluginConvertShapeToPath              = 1 << 31,
    LPSVGOPluginSortAttrs                       = 1 << 32,
    LPSVGOPluginTransformsWithOnePath           = 1 << 33,
    LPSVGOPluginRemoveDimensions                = 1 << 34,
    LPSVGOPluginRemoveAttrs                     = 1 << 35,
    LPSVGOPluginAddClassesToSVGElement          = 1 << 36,
    LPSVGOPluginRemoveStyleElement              = 1 << 37
};

最高值左移 37 位,远低于我应该可用的 63 位。


问题

我从该枚举创建一个掩码,如下所示:

LPSVGOPlugin defaultPluginMask = LPSVGOPluginNone;
        defaultPluginMask = (LPSVGOPluginCleanupAttrs
                               |LPSVGOPluginRemoveDoctype
                               |LPSVGOPluginRemoveXMLProcInst
                               |LPSVGOPluginRemoveComments
                               |LPSVGOPluginRemoveMetadata
                               |LPSVGOPluginRemoveDesc
                               |LPSVGOPluginRemoveUselessDefs
                               |LPSVGOPluginRemoveEditorsNSData
                               |LPSVGOPluginRemoveEmptyAttrs
                               |LPSVGOPluginRemoveHiddenElems
                               |LPSVGOPluginRemoveEmptyText
                               |LPSVGOPluginRemoveEmptyContainers
                               |LPSVGOPluginCleanupEnableBackground
                               |LPSVGOPluginMinifyStyles
                               |LPSVGOPluginConvertStyleToAttrs
                               |LPSVGOPluginConvertColors
                               |LPSVGOPluginConvertPathData
                               |LPSVGOPluginConvertTransform
                               |LPSVGOPluginRemoveUnknownsAndDefaults
                               |LPSVGOPluginRemoveNonInheritableGroupAttrs
                               |LPSVGOPluginRemoveUselessStrokeAndFill
                               |LPSVGOPluginRemoveUnusedNS
                               |LPSVGOPluginCleanupIDs
                               |LPSVGOPluginCleanupNumericValues
                               |LPSVGOPluginMoveElemsAttrsToGroup
                               |LPSVGOPluginMoveGroupAttrsToElems
                               |LPSVGOPluginCollapseGroups
                               |LPSVGOPluginMergePaths
                               |LPSVGOPluginConvertShapeToPath
                               );

当我使用这个记录 defaultPluginMask 的值时:

NSLog(@"maskValue: %li", (long)defaultPluginMask);

结果是-536879137。一旦我从掩码中删除最后一个掩码添加 (LPSVGOPluginConvertShapeToPath),我就会得到一个正值:1610604511

重要的是,LPSVGOPluginConvertShapeToPath 左移 31 位,这将是 32 位 NSInteger 中的最大位位置。但是,如果我使用 sizeof() 来记录 defaultPluginMask 的大小,它会将其报告为 8 个字节,这意味着它应该是 64 位。

我有点(哈!)困惑。我犯了一个我没有看到的基本错误吗?

最佳答案

对于 long数据类型,C标准规定要求最小范围从-2147483648开始至 2147483647 ,即使用 32 bits ,包括符号位。

即使针对 64 bits 进行编译平台,不保证 long变量将在 64 bits 上表示.确保它至少有 64 bits存储,long long应该使用。正如@Olaf 所建议的那样,要精确指定存储大小,stdint.h可以使用类型(例如 int64_t )。

但上述观察结果似乎并不是您的问题的根源(如果 NSIntegerlong 大小 >= 8 字节)。

问题是要移动的值 1是一个适合 32 位的整数文字,将按原样存储(如果它不适合 32 位,例如 50 亿,它将存储在 64 位上)。和 1 << 31将是一个有符号的 32 位整数,1 恰好在符号位置,因此是一个负数。如果稍后将其分配给 64 位有符号整数变量,则符号位将被扩展(二进制补码表示)。

这可以从您的案例中观察到的值看出,这些值在 64 位上以十六进制表示为:

 -536879137  =>  ffffffffdfffdfdf   - when 1 << 31 is present
 1610604511  =>          5fffdfdf   - when 1 << 31 is removed

下面我做了一个小实验,看看这个“边界”附近会发生什么(应该注意的是,从 1 << 321 << 33 获得的值是未定义的行为 - 转移存储大小,这在其他平台上可能恰好给出与 1 << 01 << 1 相同的结果。

printf("%d %d %d %d\n", sizeof(int), sizeof(long), sizeof(long long), sizeof(void*)); 
// Prints: 4 4 8 8, running on an Windows 64-bit, compiled with gcc from MinGW
long long a[9] = {
    1   << 30, //  1073741824         40000000
    1LL << 30, //  1073741824         40000000
    1   << 31, // -2147483648 ffffffff80000000
    1LL << 31, //  2147483648         80000000
    1   << 32, //           0                0
    1LL << 32, //  4294967296        100000000
    1   << 33, //           0                0
    1LL << 33, //  8589934592        200000000
    5000000000,//  5000000000        12a05f200
};
// Prints of this loop are in the comments above
for (int i = 0; i < 9; i++)
    printf("%12lli %16llx\n", a[i], a[i]);

解决方案:如果一个整型字面量打算直接用于其结果不适合该字面量存储大小的操作,则它必须带有后缀:

  • u/U对于 unsigned (如果需要)

  • l/L对于 long

  • ll/LL对于 long long

问题中的问题可以通过应用 LL 来解决。这些元素的后缀:

LPSVGOPluginConvertShapeToPath              = 1LL << 31,
LPSVGOPluginSortAttrs                       = 1LL << 32,
LPSVGOPluginTransformsWithOnePath           = 1LL << 33,
LPSVGOPluginRemoveDimensions                = 1LL << 34,
LPSVGOPluginRemoveAttrs                     = 1LL << 35,
LPSVGOPluginAddClassesToSVGElement          = 1LL << 36,
LPSVGOPluginRemoveStyleElement              = 1LL << 37

关于objective-c - 64 位 NS_OPTIONS 位掩码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33904321/

相关文章:

macos - COCOA Mac 应用程序中 NSSegmentedControl 按钮的键盘快捷键

cocoa - NSURLConnection/NSURLRequest gzip 支持

ios - 每15秒更新一次位置

ios - 如何在 CALayer 中绘制径向渐变?

c - 寻找 snprintf()-替换

c - 使用 C 并尝试创建一个疯狂的库。

arrays - 你如何从c中的数组中获取一串字符?

objective-c - NSURLConnection 线程安全吗?

ios - 在动画期间防止 UITableViewCell 布局

objective-c - 在 iOS 中循环遍历 uint8_t * 的所有元素