【问题标题】:Undefined behavior with pointer arithmetic on dynamically allocated memory动态分配内存上指针算法的未定义行为
【发布时间】:2019-06-25 00:28:56
【问题描述】:

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

从 6.5.6p7...

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

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

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) 执行无效取消引用。

【问题讨论】:

  • “因为我们取消引用超出了数组绑定”,但 p 不是 数组
  • 我知道不是;我在问题中说明了这一点。但是这里是否有一个隐含的数组,它的界限被越过了?它说 p 被视为指向数组中的一个对象。
  • 请注意,在 7.22.3.4p2 The malloc function allocates space for an object whose size is specified by size and whose value is indeterminate.。分配的空间被视为一个对象。然后获取对象指针并将其隐式转换为char *

标签: c language-lawyer c99 pointer-arithmetic


【解决方案1】:

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

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

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

【讨论】:

  • 老实说,我对这一段比什么都感到困惑。 “此类对象的访问数组”甚至是什么意思? [] 运算符是根据指针算术定义的,这意味着(根据我的原始帖子)我正在对一个指针进行算术运算,该指针暗示指向长度为 1 的数组中的对象...
  • @zagortenay333 :该引用表示malloc 的结果可以分配给任何指针类型(例如char*),然后可以使用该指针访问分配的内存该类型的数组(例如char 的数组),在分配的内存范围内。由于指针算法根据您的引号在char 的数组上得到了很好的定义,所以一切都很好。
  • 嗯。所以 7.20.3 基本上是在纠正这个?即,如果 7.20.3 不包含“此类对象的数组”部分,那么上面是 UB 是否正确?
  • @zagortenay333 malloc 返回一个对象,该对象被视为长度为1 的数组。您假设malloc 返回许多对象,其中每个对象都被视为长度为1 的数组,这是一个错误的假设。
  • @zagortenay333 malloc 在 C 中有点为您提供包含所有可能对象及其数组的内存。但是,即使例如intfloat 对象同时“存在”在同一内存位置,通过不兼容类型的左值访问它们并不违反严格的别名规则,因为它们是根据有效类型和新分配的内存没有有效类型。听起来像是一团糟,但这就是委员会设法协调动态内存与字符串别名规则的方式。
猜你喜欢
  • 2017-01-27
  • 1970-01-01
  • 2019-07-12
  • 2015-02-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多