64位NS_OPTIONS位掩码

4

背景

我正在使用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位。
我有点困惑。我是否犯了一个我没有看到的基本错误?

3
当移动32位或更多位时,请使用1ULL << 32 - nnn
搞定了!非常感谢。如果您能将其撰写为官方答案,并添加一些关于为什么需要ULL的背景知识(我查了一下,但未来查看此线程的人会从简要说明中受益),我将标记它为答案。 - Bryan
请使用 stdint.h 类型和宏。同时使用 inttypes.hprintf 转换说明符。%li 接受一个 long,但不能保证其具有超过 32 位的长度。 - too honest for this site
1个回答

6
对于long数据类型,C标准规定最小范围应为从-21474836482147483647,即使用32位,包括符号位。
即使编译为64位平台,也不能保证long变量将在64位上表示。为确保它具有最小的64位存储空间,应使用long long。正如@Olaf所建议的,为了精确指定存储大小,可以使用stdint.h类型(例如int64_t)。
但是,上述观察结果似乎不是您的情况的根源(如果NSIntegerlong大小均为>= 8字节)。
问题在于要移位的值1是适合32位的整数文字,并将以此存储(如果它不适合32位,例如50亿,则将其存储在64位上)。并且1 << 31将是一个带有1的32位有符号整数,恰好在符号位置上,因此是负数。如果稍后将其分配给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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接