先贴一段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. Discuz X3.2 分区 gid 完美伪静态方法 Apache/Nginx

    Discuz 官方给出的伪静态规则并不完整,只实现了部分的伪静态设置及规则,分区 gid 仍然是 forum.php?gid=xxx 的形式,对于有强迫症的我是无法忍受的,下面给出分区 gid 的伪静 ...

  2. php中利用正则去掉中文全角空格

    一开始用$temp = trim($temp, " "); 这种方法,导致trim后的中文字符有乱码 最后 $str = " 广东君孺律师事务所 "; $str ...

  3. android任意view爆炸效果--第三方开源--ExplosionField

    犹如天女散花一样,爆炸散列,比较有趣.Android ExplosionField在github上的项目主页是:https://github.com/tyrantgit/ExplosionField ...

  4. ORACLE DG之参数详解

    1.DB_NAME 数据库名字,需要保持同一个Data Guard中所有数据库DB_NAME相同. 主库和备库相同 DB_NAME='chicago' DB_NAME='chicago' 2.DB_U ...

  5. [Environment Build] Maven环境配置

    1. 下载并解压maven文件 2. 在环境变量中配置一个JAVA_HOME的变量,指向你本地的JDK 3. 在系统变量中新建一个名为:MAVEN_HOME的变量,指向你的maven解压文件的bin目 ...

  6. JVM学习总结四——内存分配策略

    之前几篇我们介绍了jvm的内存模型以及垃圾回收机制,而本篇我们将介绍几个JVM中对象在分配内存是应该遵循的策略.毕竟,想要去优化程序,不仅要考虑垃圾回收的过程,还要从对象内存分配的角度减少gc的代价. ...

  7. MVC4.0 利用IActionFilter实现简单的后台操作日志功能

    首先我们要了解MVC提供了4种常用的拦截器:IActionFilter(Action拦截器接口).IExceptionFilter(异常拦截器接口).IResultFilter(Result拦截器接口 ...

  8. sharepoint 2010 切换域

    前提: 现在已经有一个sharepoint 2010的环境,当前域为contosoA.com,有个需求需要将这个域切换到域contosoB.com.下面是成功操作的步骤. 1.数据最重要 备份所有数据 ...

  9. django概述

    一.django的特点 1.提供一体化的web解决方案,什么叫一体化:mvc 2.等你玩儿牛逼了可以拔插组件,换成自己顺手或者更牛逼的组件

  10. iOS 进阶 第二十二天(0603)

    0603 block\运行时 block block的本质是一个指向结构体的指针. 运行时 要分析clang命令反编译出来的c++代码,就要把一些小括号删掉来分析.因为这些小括号一般都是类型强转. o ...