(6.5.6 加法运算符) 9 当两个指针相减时,它们都必须指向同一数组对象的元素,或者是数组对象的最后一个元素之后;结果是两个数组元素的下标之差。
现在我想知道什么被认为是“数组对象”。 更具体地说,我想知道下面这个愚蠢的例子是否合法? 那个分配的内存块是否被认为是一个“数组对象”?
uint8_t *data = malloc(255);
uint8_t *end = data + 255;
ptrdiff_t size = end - data;
uint8_t *data = malloc(255);
uint8_t *end = data + 255;
ptrdiff_t size = end - data;
对于非语言法律目的,是的。
对于语言法律目的,我不认为使用 uint8_t
能够保证算术运算,但字符类型(char
、unsigned char
或 signed char
)可以。
根据 C 2018 7.22.3.4 的第 2 和第 3 条款,如果 malloc
不返回空指针,则返回值指向已分配的请求大小对象的空间。根据 3.15 的第 1 条,对象是“执行环境中的数据存储区域,其内容可以表示值”。由 malloc
提供的空间是执行环境中的数据存储区域,其内容可以表示值,即使它们还没有被初始化。
如果我们将 malloc
的结果分配给一个字符类型的指针,则会应用 6.3.2.3 第 7 条:“...当对象的指针转换为字符类型的指针时,结果指向对象的最低地址字节。对结果的连续递增,直到对象的大小,产生对对象剩余字节的指针。”虽然没有明确说明,但这被理解为意味着该对象可以被视为字符类型的数组,并且这是 C 标准的其他部分所要求的,例如 6.5 6(“如果将值复制到没有声明类型的对象中...作为字符类型的数组,...)。
因此,在此对象上,指针类型为 char *
、unsigned char *
或 signed char *
的指针算术运算是有定义的。
尽管 uint8_t
如果由 <stdint.h>
定义,必须具有与 unsigned char
大致相同的属性(两者都是纯二进制的,uint8_t
不能大于 unsigned char
,因为 unsigned char
必须支持值 255,而 uint8_t
不能小于 unsigned char
,因为字符类型根据定义是对象大小的基本单位),但它不一定是相同类型。它可以是“扩展整数类型”,如 6.2.5 第 4 条所允许的,因此可能不被关于将指针转换为字符类型的规则所覆盖。
int *ip = malloc(255 * sizeof(int));
是否是指向数组的指针?(或者是 uint32_t
。)如果我使用 malloc
分配了一些结构体类型的存储空间,那么它仍然被认为是一个数组吗? - 1201ProgramAlarmint *
,情况就不那么清晰了。标准对字符指针进行了特殊处理,即使尚未向其写入任何内容,也允许我们将分配的对象(存储区域)视为字节数组。但是,对于其他类型,它有关于“有效类型”的规定,这些规定很难解释。从实际角度来看,这种指针算术是有效的。从法律角度来看,其他人可以回答,或者我必须另外研究一下。 - Eric Postpischiluint8_t
更改为 unsigned char
- 更改纯粹是语义上的。但是,如果您必须保证某些偏门的假设机器的最严格的标准兼容性,则考虑进行更改。(@EricPostpischil - 另一个很好的答案...) - David C. Rankin如果分配成功,则返回的指针适当地对齐,以便可以将其分配给任何类型对象的指针,该对象具有基本对齐要求,然后用于访问在分配的空间中分配的此类对象或这些对象的数组(直到显式释放空间)。
(强调是我的。)
因此,由malloc
返回的内存是一个数组,像您所做的计算指针差异是合法的。
当两个指针相减时,它们都必须指向同一个数组对象或者是该数组对象的最后一个元素之后。
[C17 § 6.5.6]
否则行为未定义。标准很明确。由于malloc
返回的值可以被赋给“任何类型的对象”的指针,然后用于访问“这些对象的数组”,[C17 § 7.22.3],因此上述规则适用。