先贴一段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. php 随机显示图片的函数(实例分享)

    转自:http://www.jbxue.com/article/12695.html 发布:thatboy   来源:Net     [大 中 小] 本文分享一个php实现的随机显示图片的函数,可以将 ...

  2. 图片加载与缓存利器(自动缓存)--第三方开源-- Glide

    Android Glide使用便利,短短几行简单明晰的代码,即可完成大多数图片从网络(或者本地)加载.显示的功能需求. 使用Android Glide,需要先下载Android Glide的库,And ...

  3. 7)Java数据类型

        Java中的数据类型分为基本数据类型和引用数据类型:   1)基础数据类型有:            boolean, byte.short.char, int.float.long, dou ...

  4. 十天学会单片机Day5 IIC总线AT2402芯片(EEPROM)应用

    1.采用串行总线技术可以使系统的硬件设计大大简化.系统的体积减小.可靠性提高.同时,系统的更改和扩充极为容易. 常用的串行扩展总线有: IIC (Inter IC BUS)总线.单总线(1-WIRE ...

  5. PF_RING 总结

    1.背景 目前收包存在的问题: 第一:inpterrupt livelock, 当收到包的时候,网卡驱动程序就会产生一次中断.在大流量的情况下,操作系统将花费大量时间用于处理中断,而只有 少量的时间用 ...

  6. kettle删除资源库中的转换或者作业

    在资源库中新建转换,作业都很简单,那么加入现在不需要其中某个转换或者作业该怎么办呢? 下图是已经存在的转换跟作业 现在需要删除aa这个转换 操作步骤如下: 1.工具----资源库----探索资源库 出 ...

  7. IE浏览器各版本的CSS Hack

    IE浏览器各版本的CSS   Hack 如下示例: .test{ color:black;/*W3C*/ color:red\9;/* IE6-IE10 */ _color:black;/*IE6*/ ...

  8. Objective-C 编码规范

    Objective-C 编码规范,内容来自苹果.谷歌的文档翻译,自己的编码经验和对其它资料的总结. 概要 Objective-C 是一门面向对象的动态编程语言,主要用于编写 iOS 和 Mac 应用程 ...

  9. [DllImport("kernel32.dll")]是什么意思??

    转载自:http://blog.csdn.net/sp6645597/article/details/8683737 1.简单说明 这叫引入kernel32.dll这个动态连接库(顾名思义就是一个链接 ...

  10. UIProgressView swift

    // // ViewController.swift // UILabelTest // // Created by mac on 15/6/23. // Copyright (c) 2015年 fa ...