ISO/IEC 14882:2003: 9.6 Bit-fields [class.bit]

  1. A member-declarator of the form identifieropt : constant-expression specifies a bit-field; its length is set off from the bit-field name by a colon. The bit-field attribute is not part of the type of the class member. The constant-expression shall be an integral constant-expression with a value greater than or equal to zero. The constant-expression may be larger than the number of bits in the object representation (3.9) of the bit-field’s type; in such cases the extra bits are used as padding bits and do not participate in the value representation (3.9) of the bit-field. Allocation of bit-fields within a class object is implementation-defined. Alignment of bit-fields is implementation-defined. Bit-fields are packed into some addressable allocation unit. [Note: bit-fields straddle allocation units on some machines and not on others. Bit-fields are assigned right-to-left on some machines, left-to-right on others. ]
  2. A declaration for a bit-field that omits the identifier declares an unnamed bit-field. Unnamed bit-fields are not members and cannot be initialized. [Note: an unnamed bit-field is useful for padding to conform to externally-imposed layouts. ] As a special case, an unnamed bit-field with a width of zero specifies alignment of the next bit-field at an allocation unit boundary. Only when declaring an unnamed bit-field may the constant-expression be a value equal to zero.
  3. A bit-field shall not be a static member. A bit-field shall have integral or enumeration type (3.9.1). It is implementation-defined whether a plain (neither explicitly signed nor unsigned) char, short, int or long bit-field is signed or unsigned. A bool value can successfully be stored in a bit-field of any nonzero size. The address-of operator & shall not be applied to a bit-field, so there are no pointers to bit-fields. A non-const reference shall not be bound to a bit-field (8.5.3). [Note: if the initializer for a reference of type const T& is an lvalue that refers to a bit-field, the reference is bound to a temporary initialized to hold the value of the bit-field; the reference is not bound to the bit-field directly. See 8.5.3. ]
  4. If the value true or false is stored into a bit-field of type bool of any size (including a one bit bitfield), the original bool value and the value of the bit-field shall compare equal. If the value of an enumerator is stored into a bit-field of the same enumeration type and the number of bits in the bit-field is large enough to hold all the values of that enumeration type, the original enumerator value and the value of the bit-field shall compare equal.

最近在64bit IOS上遇到一个问题, 发现bit field的数据不对. 这个bit field是一个data struct, 在host(win32)上生成. 运行时候发现IOS取的值不对.

比如

//IOS 32
struct
{
//allocation unit 1
int a : ;
int b : ;//12bit
int c : ; //28bit
int d : ; //4 bit allocation uint 1, 4 bit allocation unit 2
int e : ; //20 bit in allocation unit 2
}; //IOS 64
struct
{
//allocation unit 1
int a : ;
int b : ;//12bit
int c : ; //28bit
//allocation unit 2
int d : ; //8 bit
int e : ; //24 bit
};

在IOS的32位上, d会横跨两个allocation uint, 而在IOS的64位上, d直接从第二个allocation unit上从新开始. clang在编译32位跟64位时的处理方式不一样,不知道是不是bug. 就算它是clang的bug, 但仍然是遵循标准的.因为C++标准说了可以这样.

根据C++标准, 这个是implementation defined behavior, 编译器可以选择横跨也可以不横跨, 都可以.

在host(win32)上的打包程序, MSVC也是横跨方式, 与clang处理32 bit IOS的方式一致. 所以(凑巧)host生成的数据可以在IOS32上面使用, 可能大多数编译器都以横跨方式处理bitfields吧.

解决方法是:将a,b,c对齐到32bit的整数, 这样强制d从新的allocation unit开始, 没有横跨的问题, 对于IOS32位和IOS64位都有效.

//shared disk data

//IOS 32
struct
{
//allocation unit 1
int a : ; //4bit
int b : ; //12bit
int c : ; //32bit
//allocation unit 2
int d : ; //8 bit
int e : ; //24 bit
}; //IOS 64
struct
{
//allocation unit 1
int a : ; //4bit
int b : ; //12bit
int c : ; //32bit
//allocation unit 2
int d : ; //8 bit
int e : ; //24 bit
};

另外, IOS 64bit的额外参考: 对齐等等其他因素

https://developer.apple.com/library/ios/documentation/General/Conceptual/CocoaTouch64BitGuide/ConvertingYourAppto64-Bit/ConvertingYourAppto64-Bit.html

https://developer.apple.com/library/ios/documentation/General/Conceptual/CocoaTouch64BitGuide/Major64-BitChanges/Major64-BitChanges.html

bitfield用在game data里面, 如果多平台共享的同一份数据话, 就有类似上面的的问题. 甚至顺序也有可能不一样(left-to-right/right-to-left), 所以blade里面没有用过. 而公司的代码用的地方非常多.

公司的代码虽然是跨平台的, 但是之前是每个平台都有不同的数据, 现在IOS 32/64要共享同一份数据, 才会有上面的问题. 这个是历史的原因.毕竟积累了近20年的代码. 注重代码和技术积累, 这样的公司才靠谱.

另外公司有自己的编译器, 最好的方式是在编译器上直接解决bitfield的straddle和left-to-right/right-to-left的问题. 这样上面的代码就不用关心这些琐碎的细节了.

个人的观点是每个平台使用通用的数据最好.

以下内容摘自我的另一博客(http://hi.baidu.com/crazii_chn/item/62705798f8a76bd91b49dfd8):

一般来说, 需要根据每一种目标平台, 各生成一份对应的数据.
这样做的好处是每个平台的数据可以根据目标特性做调整.比如移动平台的场景规模/质量和数据量一般都相对要小.
由于每种CPU的数据处理特性(cache line, alignment, SIMD指令集)可能都不一样,所以需要做特别的处理.

对于数据的格式,有两种做法, 第一是每个平台的数据包格式不一样, 第二是种是使用统一的格式, 也就是说尽管每个目标平台都有不同的一个数据包(因目标规模而定), 但理论上某个平台可以正常加载其他所有平台的数据包.
而第一种则不同, 各个平台的数据格式不一样, 比如一个struct的成员对齐不同, 那么序列化后的数据会有大小和偏移量的差别.

第一种方式不用特别考虑数据对齐等等问题, 但是要生成和维护不同目标平台下的数据包和工具, 虽然维护成本相对较低,但是如果工具很多的时候也很繁琐.
第二种方式需要在代码中考虑对齐等等因素, 保证一个数据包可以被所有的平台加载, 这么做可能也很繁琐,需要特别小心,并经过完整的数据测试.

这里有一个android下x86和arm的结构对齐的处理 (4.1 Forced Memory Alignment):
http://software.intel.com/en-us/articles/android-application-development-and-optimization-on-the-intel-atom-platform
参考http://software.intel.com/en-us/blogs/2011/08/18/understanding-x86-vs-arm-memory-alignment-on-android/
比如上面第一个链接中使用的"-malign-double" GCC参数, 强制8字节的数据对齐的8字节边界.保证同一份数据可以被不同的CPU正常加载. 第二个链接使用的是__attribute__((aligned(n))), 来强制数据成员的对齐.

我所在的公司使用的是第一种方式. 第一种方式的问题在于同一个OS也有不同的target CPU(platform), 这样会出现对于android平台, x86有一份数据, 而arm有另外一份数据. 所以个人倾向于第二种方式, 除了上文链接中的方式以外, 也可以强制不读写struct, 只读写原子数据, 把一个struct的成员逐个单独读写.

如何保证数据能够通用呢? 严格按照C/C++标准来做. 如果是implementation defined或者unspecified的定义, 那么就规避, 不要使用, 比如上面的那个例子. undefined behavior更不能用. 这样在host编译出的data-tools, 处理数据的方式与target上的executables处理数据的方式一致, 能够保证host上打包生成的同一份数据, 在所有平台上都可以用.

[工作积累] bitfield的更多相关文章

  1. [工作积累] Google/Amazon平台的各种坑

    所谓坑, 就是文档中没有标明的特别需要处理的细节, 工作中会被无故的卡住各种令人恼火的问题. 包括系统级的bug和没有文档化的限制. 继Android的各种坑后, 现在做Amazon平台, 遇到的坑很 ...

  2. [工作积累] 32bit to 64bit: array index underflow

    先贴一段C++标准(ISO/IEC 14882:2003): 5.2.1 Subscripting: 1 A postfix expression followed by an expression ...

  3. [工作积累] GCC 4.6 new[] operator内存对齐的BUG

    对于用户没有定义dctor(包括其所有成员)的类来说, new CLASS[n] 可能会直接请求sizeof(CLASS)*n的空间. 而带有dctor的 类, 因为delete[]的时候要逐个调用析 ...

  4. [工作积累] UE4 并行渲染的同步 - Sync between FParallelCommandListSet & FRHICommandListImmediate calls

    UE4 的渲染分为两个模式1.编辑器是同步绘制的 2.游戏里是FParallelCommandListSet并行派发的. mesh渲染也分两类,static mesh 使用TStaticMeshDra ...

  5. [工作积累] D3D10+ 中 Pixel Shader 的input semantic和参数顺序

    由于semantic的使用,我们有理由相信 vertex shader的output 和 pixel shader的input是按照semantic来匹配的,而跟传入顺序无关.印象dx9时代是这样. ...

  6. [工作积累] shadow map问题汇总

    1.基本问题和相关 Common Techniques to Improve Shadow Depth Maps: https://msdn.microsoft.com/en-us/library/w ...

  7. linux-日常工作积累

    Linux常用命令之envsubst https://blog.csdn.net/banche163/article/details/101369495 Linux中的EAGAIN含义 https:/ ...

  8. HAL层Camera模块Dump图片--工作积累

    Camera的raw data一般都是YUV420的格式,数据的特点是: YUV 4:2:0采样,每四个Y共用一组UV分量 YUV420格式: 先Y,后V,中间是U.其中的Y是w * h,U和V是w/ ...

  9. JS工作积累

    /* * YYYY-MM-DD类型的字符串日期比较 * */ String.prototype.compareAsDate=function(dateStr){//prototype原型方法 var ...

随机推荐

  1. BufferedInputSream和BufferedOutputSream,,,

    package cd.itcast.bufferinputstream; import java.io.BufferedInputStream; import java.io.File; import ...

  2. 使用@media做自适应

    @media (min-width: 768px){ //>=768的设备 }    @media (max-width: 1199){ //<=1199的设备 }

  3. WPF学习03:Element Binding

    元素绑定是数据绑定的一个子集,关于数据绑定是什么,园子里有很多文章都说得非常好,在此不予详细说明. WPF实现了完善的数据绑定机制,使得元素绑定有简易的实现步骤. 本文的元素指的是WPF中的可视控件. ...

  4. C#调用C++ Dll

    现在项目基本都是旁边C++的哥们做好dll扔给我,然后我调用.好久之前晚上down了一份c#调用c++dll的方法,出处早已经遗忘.闲来无事,放上来好了.原作者看到后可以留言,我会把您链接放上的,帮了 ...

  5. JavaWeb之 JSP:自定义标签

    当jsp的内置标签和jstl标签库内的标签都满足不了需求,这时候就需要开发者自定义标签. 自定义标签 下面我们先来开发一个自定义标签,然后再说它的原理吧! 自定义标签的开发步骤 步骤一 编写一个普通的 ...

  6. java的基本数据类型特征

    java的数据类型分为基本数据类型和引用数据类型. 基本数据类型分为数值型.字符型(char).布尔型(boolean) 数值型变量 1.整数型 类型 占用存储空间 表示范围 byte 1字节Byte ...

  7. Redis客户端之Spring整合Jedis

      1.下载相关jar包,并引入工程: jedis-2.4.2.jar commons-pool2-2.0.jar 2.将以下XML配置引入spring <bean id="shard ...

  8. hdu 1008

    题目意思是:给你N个数字  每个数字表示多少层楼  现在要你从0层楼开始坐电梯  一次按顺序走过这些楼层 规则是 上楼6秒 ,下楼4秒,每次到达一个楼层停5秒..... 思路:模拟 代码如下:(要注意 ...

  9. chattr 与 lsattr 命令详解

    PS:有时候你发现用root权限都不能修改某个文件,大部分原因是曾经用chattr命令锁定该文件了.chattr命令的作用很大,其中一些功能是由Linux内核版本来支持的,不过现在生产绝大部分跑的li ...

  10. [原创]PostgreSQL Plus Advince Server在 HA环境中一对多的Stream Replication配置(四)

    八.HA环境下配置多节点的sshVIP(s1):[root@s1 ~]# mkdir /opt/PostgresPlus/9.2AS/.ssh[root@s1 ~]# chown enterprise ...