先贴一段C++标准(ISO/IEC 14882:2003):

5.2.1 Subscripting:

1 A postfix expression followed by an expression in square brackets is a postfix expression. One of the
expressions shall have the type “pointer to T” and the other shall have enumeration or integral type. The
result is an lvalue of type “T.” The type “T” shall be a completely-defined object type.56) The expression
E1[E2] is identical (by definition) to *((E1)+(E2)). [Note: see 5.3 and 5.7 for details of * and + and
8.3.4 for details of arrays. ]

5.7 Additive operators:

5 When an expression that has integral type is added to or subtracted from a pointer, the result has the type of the pointer operand. If the pointer operand points to an element of an array object, and the array is large enough, the result points to an element offset from the original element such that the difference of the subscripts of the resulting and original array elements equals the integral expression. In other words, if the expression P points to the i-th element of an array object, the expressions (P)+N (equivalently, N+(P)) and (P)-N (where N has the value n) point to, respectively, the i+n-th and i–n-th elements of the array object, provided they exist. Moreover, if the expression P points to the last element of an array object, the expression (P)+1 points one past the last element of the array object, and if the expression Q points one past the last element of an array object, the expression (Q)-1 points to the last element of the array object. If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined.

最近工作中遇到的问题是在64位IOS上:

 uint32 i;
...
char* n = (char*)&array[i-] + n;

当i为0的时候, 下标溢出.

对照标准, 可以看出, array[i-1]相当于*(array+uint32(i-1)), 当i-1计算溢出时, 下标的数值很大(0xFFFFFFFFU), 数组的寻址也溢出了, 结果是undefined behavior.

然而在32位上的结果正确, 为什么呢?

因为0xFFFFFFFF用来寻址的时候, 对于大多数32位CPU来说, 都是带符号的数值, 所以0xFFFFFFFF被CPU解释为-1.

这个时候array[0xFFFFFFFF]相当与array[-1].

然而对于C++来说, 这只是一个巧合. 根据标准定义, 他是undefined behavior. 所以在64位上出错也不奇怪.

对于64位, 当i为0时, 下标计算结果仍然是0xFFFFFFFF, 然而64位上的-1是0xFFFFFFFFFFFFFFFF, 显然0xFFFFFFFF是一个很大的正整数, 导致真正溢出.

解决办法是它转换为带符号数, 即: array[ int(i-1) ]. 这样的结果是-1, 而不是0xFFFFFFFFU.

回想blade的下标索引, 基本上都用的是size_t:

 typedef size_t index_t;
index_t i;

因为size_t在64位系统上是64字节, 所以i-1得到的结果是0xFFFFFFFFFFFFFFFF, 这样也能保证基本上不出错.

然而这么做仍然是undefined behavior, 因为0xFFFFFFFFFFFFFFFF在用作寻址的时候, 是作为有符号数还是无符号数, 这个是由CPU来定义的, 虽然大部分CPU为了灵活都是用的是带符号的.

但是这是CPU的细节. 假如现在有一个公司, 如BMD或者UNTEL公司生产了新型的CPU, 其对与类似

mov eax, [ebx + esi*]
lea eax, [ebx + esi]

的指令, 把索引寄存器(例如esi)解释为无符号数, 而在地址溢出时做了处理, 比如CPU异常信号或者地址回卷等等, 那么结果又很难说了.

这已经设计到CPU的细节, 超出了C++可控的范围, 所以C++标准才认为这是undefined behavior.

最好的方式是用带符号的类型, 比如int/long/long long/ptrdiff_t来作为下标, 当然有时候不能控制下标变量i的类型(来源), 那么在使用时注意转换.

或者, 将&array[i-1]改为 array + i - 1,  注意不是array + (i-1)

这样能够避免i为0时的溢出.

[工作积累] 32bit to 64bit: array index underflow的更多相关文章

  1. 通过加载Kernel32来动态判断 当前操作系统32bit还是64bit

    工作原理:通过加载Kernel32来获取IsWow64Process 函数然后通过函数的地址操作,执行函数的操作. 在程序中只要我们获取了一个函数的地址,就可以找到正确的方法执行这个函数. 但是这种方 ...

  2. 【PC-x86-x64】JDK 32bit与64bit的区别及x64 PC的发展历程【转】

    一次偶然分析的机会: 在进行Minecraft也就是所谓的我的世界游戏的时候,在对局域网进行开放的时候,我的是64bit的JDK,而我同学的是32bit的JDK,所以在进行局域网链接的时候就会出现In ...

  3. Linux Kernel sys_call_table、Kernel Symbols Export Table Generation Principle、Difference Between System Calls Entrance In 32bit、64bit Linux

    目录 . sys_call_table:系统调用表 . 内核符号导出表:Kernel-Symbol-Table . Linux 32bit.64bit环境下系统调用入口的异同 . Linux 32bi ...

  4. MySQL-python 1.2.3 for Windows and Python 2.7, 32bit and 64bit versions -(亲测可用)

    MySQL-python 1.2.3 for Windows and Python 2.7, 32bit and 64bit versions - See more at: http://www.co ...

  5. 使用asp.net MVC的 HtmlHelper 时遇到的小问题,报错:Templates can be used only with field access, property access, single-dimension array index, or single-parameter custom indexer expressions.

    异常信息:Templates can be used only with field access, property access, single-dimension array index, or ...

  6. How to Check if Linux (Ubuntu, Fedora Redhat, CentOS) is 32-bit or 64-bit

    The number of CPU instruction sets has kept growing, and likewise for the operating systems which ar ...

  7. Linux Kernel sys_call_table、Kernel Symbols Export Table Generation Principle、Difference Between System Calls Entrance In 32bit、64bit Linux【转】

    转自:http://www.cnblogs.com/LittleHann/p/4127096.html 目录 1. sys_call_table:系统调用表 2. 内核符号导出表:Kernel-Sym ...

  8. 怎么查看ubuntu是32bit还是64bit的?

    怎么查看ubuntu是32bit还是64bit的?你用uname -a的时候看到的i686就是32bit的----

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

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

随机推荐

  1. Css 书写规范【转】

    1. 不同浏览器元素的默认属性有所不同,使用Reset可重置浏览器元素的一些默认属性,以达到浏览器的兼容. /** 清除内外边距 **/ body, h1, h2, h3, h4, h5, h6, h ...

  2. WPF中多窗口共享静态属性

    由于我的DoubanFm在重新考虑之后,需要设置一个全局的CurrentSong,这个字段要让所有的VM都知道,而我同时又想把它作为我所有VM的共有属性.而且我想尽量减少代码的复制,提高重用.所以我做 ...

  3. 一篇文章教你读懂Makefile

    makefile很重要      什么是makefile?或许很多Winodws的程序员都不知道这个东西,因为那些Windows的IDE都为你做了这个工作,但我觉得要作一个好的和professiona ...

  4. Android:ViewPager实现屏幕轮转和使用PagerTabStrip

    ① ViewPager类直接继承了ViewGroup类,所有它是一个容器类,可以在其中添加其他的view类. ② ViewPager类需要一个PagerAdapter适配器类给它提供数据. ③ Vie ...

  5. hdu 5264 pog loves szh I

    题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=5264 pog loves szh I Description Pog has lots of stri ...

  6. 在meteor中如何使用ionic组件tabs,及如何添加使用cordova plugin inappbrower

    更新框架: meteor update meteor框架的优点不言而喻,它大大减轻了App前后端开发的负担,今年5月又获得B轮2000万融资,代表了市场对它一个免费.开源开发框架的肯定.cordova ...

  7. python的内存管理

    1.在Python中,整数和短小的字符,Python都会缓存这些对象,以便重复使用.当我们创建多个等于1的引用时,实际上是让所有这些引用指向同一个对象. a = 1 b = 1 print hex(i ...

  8. 一些Iphone sqlite 的包装类

    相信很多人用iphone的Sqlite不会直接用C的方法,要么自己包装一层Object c的访问方法,要么用CoreData,下面我整理些目前所了结的一些Sqlite 包装类.  1.CoreData ...

  9. Hive与HBase区别

    对于刚接触大数据的用户来说,要想区分Hive与HBase是有一定难度的.本文将尝试从其各自的定义.特点.限制.应用场景等角度来进行分析,以作抛砖引玉之用. ====Hive是什么?Apache Hiv ...

  10. power tool 强制撤销

    安装以后,使用有权限的账号,最好在tfs服务器安装并操作使用admin账号登陆项目,在团队资源管理器,源码管理中选择需要撤销的文件父文件夹,选Find in Source Control=>St ...