关于C++标准中动态数组指针算术定义的技术问询
嘿,这个问题问得特别抠细节——我当初第一次死磕C++标准的时候也被这点绕晕过!
你观察到的点完全准确:C++标准的[expr.add]章节里,指针算术的规则确实绑定到了“数组对象(array object)”的概念上,而翻[dcl.array]章节时,看起来它只定义了用T arr[常量表达式]这种语法声明的数组(包括空大小的情况,比如T arr[]),完全没提T* arr = new T[运行时大小]这种动态数组。但这里的关键是:标准里的“数组对象”概念,范围比你从[dcl.array]里看到的要宽得多。
其实动态分配的数组,完完全全属于标准定义的“数组对象”——只是它的创建逻辑不在[dcl.array]里,而是在[expr.new](new表达式对应的章节)里。标准里明确说了,当你用new T[n](n可以是运行时计算的无符号整数)时,这个表达式会创建一个包含n个T类型元素的数组对象,然后返回指向该数组首元素的指针。
而[expr.add]里的指针算术规则,是针对所有“数组对象”的,不管这个数组是怎么创建的:
- 只要你有一个指向动态数组某元素的指针P,那么P+J、J+P(当结果落在
0 ≤ i+j ≤ n范围内,i是P指向的元素下标,n是数组大小)就会指向对应的数组元素(或者尾后那个“假设的元素”); - P-J的规则同理,只要结果在合法范围内就是定义良好的;
- 超出这个范围的话,行为依然是未定义的。
那为什么[expr.add]里没专门提动态数组呢?因为C++标准喜欢用抽象概念来覆盖所有等价情况——不管数组是栈上静态声明的、全局的、还是动态分配的,只要它是“数组对象”,就适用同一套指针算术规则,没必要把每种创建方式都列一遍。
举个直白的例子:
// 静态数组 int static_arr[5]; int* p1 = static_arr; p1 + 3; // 合法,指向第4个元素 p1 + 5; // 合法,尾后指针 p1 + 6; // 未定义行为 // 动态数组 int* dynamic_arr = new int[5]; int* p2 = dynamic_arr; p2 + 3; // 完全合法,和静态数组规则一致 p2 + 5; // 合法的尾后指针 p2 + 6; // 同样是未定义行为
简单说,标准用“数组对象”这个抽象概念把静态和动态数组的指针算术行为统一了,只是动态数组的创建逻辑在[expr.new]而不是[dcl.array]里,所以你一开始没把这两部分关联起来而已。
内容来源于stack exchange

