首页 > 解决方案 > 动态分配内存上指针算法的未定义行为

问题描述

我可能误解了这一点,但是 c99 规范是否会阻止对动态分配的内存进行任何形式的指针运算?

从 6.5.6p7...

出于这些运算符的目的,指向不是数组元素的对象的指针与指向长度为 1 且对象类型作为其元素类型的数组的第一个元素的指针的行为相同。

...指向不在数组中的对象的指针被视为指向一个包含 1 项的数组(使用运算符 + 和 - 时)。然后在这个片段中:

char *make_array (void) {
    char *p = malloc(2*sizeof(*p));
    p[0] = 1; // valid
    p[1] = 2; // invalid ?
    return p;
}

...第二个下标p[1]无效?由于p指向不在数组中的对象,因此将其视为指向包含一项的数组中的对象,然后从 6.5.6p8 ...

当一个整数类型的表达式被添加到指针或从指针中减去时,结果具有指针操作数的类型。如果指针操作数指向数组对象的元素,并且数组足够大,则结果指向与原始元素偏移的元素,使得结果和原始数组元素的下标之差等于整数表达式。换句话说,如果表达式 P 指向数组对象的第 i 个元素,则表达式 (P)+N(等效于 N+(P))和 (P)-N(其中 N 的值为 n)指向分别到数组对象的第 i+n 个和第 i-n 个元素,前提是它们存在。此外,如果表达式 P 指向数组对象的最后一个元素,则表达式 (P)+1 指向数组对象的最后一个元素,如果表达式 Q 指向数组对象的最后一个元素,则表达式 (Q)-1 指向数组对象的最后一个元素。如果指针操作数和结果都指向同一个数组对象的元素,或者超过数组对象的最后一个元素,则计算不应产生溢出;否则,行为未定义。如果结果指向数组对象的最后一个元素,则不应将其用作计算的一元 * 运算符的操作数。

...我们有未定义的行为,因为我们取消了对数组边界的引用(暗示长度为 1 的那个)。

编辑:

好的,为了澄清更多让我感到困惑的地方,让我们一步一步来:

1.)p[1]定义为*(p+1).
2.)p指向不在数组内部的对象,因此它被视为指向长度为 1 的数组内部的对象,以便评估p+1.
3.)p+1产生一个指针 1 越过 p 暗示指向的数组。
4.) *(p+1)做无效的取消引用。

标签: clanguage-lawyerc99pointer-arithmetic

解决方案


C99,7.20.3 - 内存管理功能(强调我的):

如果分配成功,则返回的指针经过适当对齐,以便可以将其分配给指向任何类型对象的指针,然后用于访问已分配空间中的此类对象或此类对象的数组(直到空间被显式释放) .

这意味着分配的内存可以作为char(根据您的示例)的数组访问,因此指针算术定义明确。


推荐阅读