我正在使用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位.
解决方法
即使编译64位平台,也不能保证长变量将在64位上表示.为确保它具有最小64位存储空间,应使用long long.正如@Olaf建议的那样,为了精确指定存储大小,可以使用stdint.h类型(例如int64_t).
但是上面的观察结果似乎并不是你问题的根源(如果NSInteger和长大小是> = 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
下面我做了一个小实验,看看在这个“边界”附近会发生什么(应该注意的是,从1 <<< 32<<<<<<<<<<<<<<<<<<<<<在其他平台上可能发生与1 <<< 0<<<<<<<< 1< 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表示未签名(如果需要)
> l / L很长时间
> ll / LL很长一段时间
可以通过将LL后缀应用于这些元素来解决问题中的问题:
LPSvgoPluginConvertShapetoPath = 1LL << 31,LPSvgoPluginSortAttrs = 1LL << 32,LPSvgoPluginTransformsWithOnePath = 1LL << 33,LPSvgoPluginRemoveDimensions = 1LL << 34,LPSvgoPluginRemoveAttrs = 1LL << 35,LPSvgoPluginAddClassesToSVGElement = 1LL << 36,LPSvgoPluginRemoveStyleElement = 1LL << 37