提问人:Sasha 提问时间:10/6/2023 最后编辑:Sasha 更新时间:10/7/2023 访问量:114
C 语言中的指针比较
Pointer comparison in C
问:
如果我分配了类似的东西
size_t n = ???;
unsigned char* s = malloc(n);
将指针与位置进行比较是否是完美定义的行为,因为当且仅当?可能是这样,但有人读到指针比较只在传染性数组中定义,对于初学者来说,不清楚上面分配的东西是否算作“数组”,因为该术语在 C 中也有正式的含义,而且人们阅读虚拟内存时还没有完全消化它, 并开始担心......所以我想问问确认一下。s + i
0 <= i < n
s + i < s + j
i < j
答:
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 中比较两个指针,无论它们指向哪里。但是,如何在不使用指针算术的情况下让指针指向这个没有类型的块呢?它会在曾经发布过的每个半体面的编译器上正常工作吗?
是的。
评论
malloc()
char
for(size_t i=0; i<n; i++) s[i] = something;
s[i]
*(s+i)
*(s+i)
从 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.
评论
const unsigned char* s = malloc(n);
评论
s
NULL
0 <= i <= n
s[n]
<