C 语言中的指针比较

Pointer comparison in C

提问人:Sasha 提问时间:10/6/2023 最后编辑:Sasha 更新时间:10/7/2023 访问量:114

问:

如果我分配了类似的东西

 size_t n = ???;
 unsigned char* s = malloc(n);

将指针与位置进行比较是否是完美定义的行为,因为当且仅当?可能是这样,但有人读到指针比较只在传染性数组中定义,对于初学者来说,不清楚上面分配的东西是否算作“数组”,因为该术语在 C 中也有正式的含义,而且人们阅读虚拟内存时还没有完全消化它, 并开始担心......所以我想问问确认一下。s + i0 <= i < ns + i < s + ji < j

c malloc 指针算术

评论

3赞 pmg 10/6/2023
是的,它是合法的,并且符合您的期望。
3赞 chux - Reinstate Monica 10/6/2023
只要不是,指针比较就比另外一个好:。只是无法访问.sNULL0 <= i <= ns[n]
0赞 BoP 10/6/2023
至少在有些系统中,不同的阵列可能最终位于不同的内存段中。然后将它们进行比较将没有意义。<
0赞 Sasha 10/7/2023
@chux-恢复莫妮卡是的,对不起,没有...对不起,P 是 S...
0赞 Sasha 10/7/2023
@chux-恢复莫妮卡关于 i = n;实际上,难道不会有一个“噩梦”场景(我猜在标准机器上不太可能),其中分配恰好在可能的指针大小的末尾,因此我们有 s + n = 0 的溢出?然后你会得到 s > s + n...

答:

3赞 Lundin 10/6/2023 #1

C标准在这里有它的问题。因为从 malloc 返回的只是一块没有声明类型的内存。从理论上讲,它不是数组或任何其他类型(还)。在实践中,编译器必须将其视为数组,否则整个 C 语言就会分崩离析。在设计 C99 标准时,没有人想到这一点,从那时起,ISO C 工作组一直回避解决这个问题。

要判断某物是否为有效访问,我们需要知道它的类型,或者如果没有像 case 那样声明的类型,那么至少我们需要知道有效类型,这是 C 标准随 C99 一起推出的系统,用于解决此类场景。malloc

从形式上讲,指向没有声明类型的内存位置。C17 6.5 §6 然后说:s

对于对没有声明类型的对象的所有其他访问,对象的有效类型只是用于访问的左值的类型。

但是你无法访问它,所以它也没有有效的类型。由于它既没有声明类型,也没有有效类型,因此它也不能是某种类型的数组。根据 C17 6.5.6 §8,执行不是数组的指针算术是未定义的行为。除非您首先访问它,从而将其标记为某种有效类型。s

显然,我们不能从字面上理解 C 标准——它被打破了。具体来说,它存在以下缺陷:

  • 6.5 §6 没有涉及如何处理“聚合类型”(数组、结构体)或如何处理类型限定符(const、volatile),就有效类型而言。
  • 6.5.6 §8 不支持对没有有效类型的项进行指针算术。

因此,为了生成某种有意义的可执行文件,编译器会忽略所有这些内容,并将返回的任何内容视为数组。同样,编译器倾向于在没有声明类型的区域上支持指针算术,否则 C 语言中与硬件相关的编程也是不可能的。malloc

所以总结一下:

  • 在这个分配的块内执行指针算术会是完美定义的行为吗?
    不。

  • 你能比较指向块的两个指针吗?
    是的,你总是可以在 C 中比较两个指针,无论它们指向哪里。但是,如何在不使用指针算术的情况下让指针指向这个没有类型的块呢?

  • 它会在曾经发布过的每个半体面的编译器上正常工作吗?
    是的。

评论

0赞 John Bollinger 10/6/2023
也许它就在那里,但我很难找到这解决了所提出的问题,即关于指向同一分配对象的指针的比较。
1赞 Lundin 10/6/2023
@JohnBollinger 从形式上讲,您可以使用关系运算符进行比较,但不能使用指针算术来定位用于比较的假定数组的部分。
0赞 chux - Reinstate Monica 10/6/2023
“无论 malloc 以数组形式返回什么” --> 嗯,返回的是指针,而不是数组。建议重新措辞。malloc()
0赞 John Bollinger 10/6/2023
@Lundin,在这种情况下,您可以使用指针进行指针算术。至少,我是这样阅读 C23 6.3.2.3/7 的。char
0赞 Lundin 10/6/2023
@chux-恢复莫妮卡:不,它写得正确。编译器必须能够假定对象是数组(或结构),即使它没有有效的类型。否则,您将无法(其中 equals 和 6.5.6 加法运算符适用)初始化 malloc 返回的段。这反过来又会破坏整个 C 语言并使其变得无用。如果我们只是按照有效类型的规则,那么在不是数组的东西上就是指针算术。for(size_t i=0; i<n; i++) s[i] = something;s[i]*(s+i)*(s+i)
4赞 Vlad from Moscow 10/6/2023 #2

从 C 标准 (3.术语、定义和符号)

3.15

1 执行环境中数据存储的对象区域, 其内容可以表示值

2 注意 当被引用时,一个对象可以被解释为具有 特定类型;见6.3.2.1。

和(7.22.3 内存管理功能)

1 连续调用 aligned_alloc、calloc、malloc 和 realloc 函数是 未指定。如果分配成功,则返回的指针为 适当对齐,以便可以将其分配给指向任何类型的指针 具有基本对齐要求的对象,然后用于 访问空间中的此类对象或此类对象的数组 已分配(直到显式解除分配空间)。

此外,此报价在这里也很有用(6.2.5 类型)

20 可以从对象构造任意数量的派生类型,并且 函数类型,如下所示:

— 数组类型描述连续分配的非空集合 具有特定成员对象类型(称为元素类型)的对象。 每当数组类型为 指定。数组类型的特征在于其元素类型和 数组中的元素数。数组类型被称为 派生自其元素类型,如果其元素类型为 T,则数组 type 有时称为 ''T 数组''。数组的构造 元素类型中的类型称为“数组类型派生”。

最后 *6.5.6 加法运算符)

8 When an expression that has integer 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 integer 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. If the result points one past the last element of the array object, it shall not be used as the operand of a unary * operator that is evaluated.

评论

0赞 Lundin 10/6/2023
The problem is that there is formally no array object in sight, based on the expression . There is just a pointer to an object of no declared type.const unsigned char* s = malloc(n);
1赞 Vlad from Moscow 10/6/2023
@Lundin As written in the note "2 NOTE When referenced, an object may be interpreted as having a particular type; see 6.3.2.1."
0赞 Lundin 10/6/2023
That refers to lvalue access which first of all assumes that the object is actually accessed, which doesn't happen here. And even if it is, that in turn calls upon the rules in 6.5 for effective type which in turn are insufficient to describe how the compiler should act.
0赞 Vlad from Moscow 10/6/2023
@Lundin As it is written an object is a region of data storage and can be interpreted as having a particular type. So accessing a region of data storage using a pointer of the type char * the data stored in the region is interpreted as data of the type char. And "An array type describes a contiguously allocated nonempty set of objects with a particular member object type"
0赞 Lundin 10/6/2023
No, as it is written, I quote "When referenced, an object may...". Referenced means de-referenced. A lvalue access as defined in 6.3.2.1 refers to (among other things) accessing a certain memory location through a de-referenced pointer to a certain type. None of it applies here. And even if it did, the standard is still unclear.