64位NS_OPTIONS位掩码

时间:2015-11-24 21:38:30

标签: 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位。

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

1 个答案:

答案 0 :(得分:5)

对于long数据类型,C标准规定最小范围必须从-21474836482147483647,即使用32 bits,包括符号位。

即使正在为64 bits平台进行编译,也无法保证在long上显示64 bits变量。为确保其存储空间最小64 bits,应使用long long。正如@Olaf建议的那样,为了精确指定存储大小,可以使用stdint.h类型(例如int64_t)。

但上述观察结果似乎并不是您案例中的问题的根源(如果NSIntegerlong尺寸>> 8字节)。

问题是要移位的值1是一个整数字,它适合32位并且将被存储(如果它不适合32位,例如5亿,它将存储在64位)。并且1 << 31将是带符号的32位整数,其中1位于符号位置,因此为负数。如果稍后将其分配给64位有符号整数变量,则符号位将被扩展(两个补码表示)。

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

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

我已经做了一个小实验,看看这个&#34;边界附近发生了什么?&#34; (应该注意,从1 << 321 << 33获得的值是未定义的行为 - 转移存储大小,在其他平台上可能会发生与1 << 0和{{相同的结果1}})。

1 << 1

解决方案:如果要将整数文字直接用于其结果不符合该文字的存储大小的操作中,则必须将其后缀为:

    {li>

    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(如果需要)

    {li>

    unsigned / l L

    {li>

    long / ll LL

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

LL