换句话说,在做
时index = &array[x] - &array[0];
是否始终保证(根据C标准)& array [0]< =& array [x],还是依赖于编译器? 与此主题相关的C标准章节是什么?
答案 0 :(得分:10)
保证地址排序。关系运算符的行为在C11 6.5.8p5:
中定义[...]具有较大下标值的数组元素的指针比具有较低下标值的同一数组的元素的指针要大。 [...]
因此,&array[x] >= &array[0]
始终为x
是元素的索引,或者大于最大索引的索引。 (如果x
指向实际数组之外,则行为未定义。)
但令人惊讶的是差异 &array[x] - &array[0]
仅在
x
是元素的实际索引或大于数组和 x
不大于PTRDIFF_MAX
因为有一个特殊的角落案例:C11 6.5.6p9说
9当减去两个指针时,两个指针都指向同一个数组对象的元素,或者指向数组对象的最后一个元素的元素;结果是两个数组元素的下标的差异。 结果的大小是实现定义的,其类型(有符号整数类型)在
ptrdiff_t
标头中定义为<stddef.h>
。如果结果在该类型的对象中无法表示,则行为未定义。换句话说,如果表达式P和Q分别指向数组对象的第i个和第j个元素,表达式(P) - (Q)的值为 ij,前提是该值适合ptrdiff_t
类型的对象。 [...]
如果签名的ptrdiff_t
与无符号size_t
的宽度相同,则可能会有一个数据存在大于x
的索引PTRDIFF_MAX
;然后&array[x] >= &array[0]
仍然存在,但&array[x] - &array[0]
有完全未定义的行为。
这是一个演示。我的计算机是运行64位Ubuntu Linux的x86-64,但它也能运行32位程序。在32位X86 Linux + GCC中,ptrdiff_t
是32位有符号整数,size_t
是32位无符号整数。在32位模式下在64位Linux中运行的程序可以使用malloc轻松分配超过2G的内存,因为整个4G地址空间是为用户模式保留的。
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <stddef.h>
int main(void) {
size_t size = (size_t)PTRDIFF_MAX + 2;
size_t x = (size_t)PTRDIFF_MAX + 1;
char *array = malloc(size);
if (! array) {
perror("malloc");
exit(1);
}
array[0] = 42;
array[x] = 84;
printf("&array[0]: %p\n", (void *)&array[0]);
printf("&array[x]: %p\n", (void *)&array[x]);
printf("&array[x] >= &array[0]: %d\n", &array[x] >= &array[0]);
printf("&array[x] - &array[1]: %td\n", &array[x] - &array[1]);
printf("&array[x] - &array[0]: %td\n", &array[x] - &array[0]);
printf("(&array[x] - &array[0]) < 0: %d\n", (&array[x] - &array[0]) < 0);
}
然后编译为32位模式并运行:
% gcc huge.c -m32 -Wall && ./a.out
&array[0]: 0x77567008
&array[x]: 0xf7567008
&array[x] >= &array[0]: 1
&array[x] - &array[1]: 2147483647
&array[x] - &array[0]: -2147483648
(&array[x] - &array[0]) < 0: 1
内存已成功分配,起始地址为0x77558008,&array[x]
为0xf7504008
,&array[x]
大于&array[0]
。差异&array[x] - &array[1]
产生了积极的结果,而&array[x] - &array[0]
及其未定义的行为现在产生了否定的结果!
答案 1 :(得分:3)
首先,FWIW,引用C11
,章节§6.5.6/ P9,( emphsis mine )
当减去两个指针时,两个指针都指向同一个数组对象的元素, 或者超过数组对象的最后一个元素;结果是的差异 这两个数组元素的下标。 [...]
因此,您不需要为单个指针值(定位)本身而烦恼。重要的是差异(即类似|a-b|
)
那就是说,如果必须进行“比较”,(关系运算符的使用,<
,>
,<=
,>=
),标准说,
当比较两个指针时,结果取决于中的相对位置 指向的对象的地址空间。 [....]如果指向的对象是同一聚合对象的成员,[...]和指向具有较大下标的数组元素的指针 值比指向具有较低下标值的相同数组的元素的指针大。 [....]
因此,对于&array[x] <= &array[0]
之类的语句,当0
时,它将评估为x > 0
( FALSY )。
答案 2 :(得分:2)
是的,因为&array[x]
被定义为等同于array+x
。
后缀表达式后跟方括号[]中的表达式 是数组对象元素的下标。该 下标运算符[]的定义是E1 [E2]与...相同 (*((E1)+(E2)))。由于适用于的转换规则 binary +运算符,如果E1是数组对象(等效于指针 到数组对象的初始元素),E2是一个整数, E1 [E2]表示E1的第E2个元素(从零开始计数)。
答案 3 :(得分:2)
C11标准将数组元素之间的地址差异定义为取决于元素的相对(逻辑)顺序的数字。正如additive operators:
的说明中所述当减去两个指针时,两个指针都应该指向 相同的数组对象,或者超过数组对象的最后一个元素; 结果是两个数组下标的差异 元素。结果的大小是实现定义的,它的 type(有符号整数类型)是ptrdiff_t中定义的 头。如果结果在该类型的对象中无法表示, 行为未定义。换句话说,如果表达式为P和Q. 分别指向数组对象的第i个和第j个元素, 表达式(P) - (Q)具有值i-j,条件是该值适合于 ptrdiff_t类型的对象。此外,如果表达式P指向任何一个 到一个数组对象的元素或一个超过一个的最后一个元素 数组对象,表达式Q指向最后一个元素 相同的数组对象,表达式((Q)+1) - (P)具有相同的值 ((Q) - (P))+ 1和 - ((P) - ((Q)+1)),如果是,则值为零 表达式P指向一个超过数组对象的最后一个元素, 即使表达式(Q)+1没有指向该元素 数组对象。
因此,您的示例中的差异是已定义为x - 0
。
答案 4 :(得分:2)
来自C11规范(ISO / IEC 9899:2011(E))§6.5.8/ 5:
当比较两个指针时,...如果指向的对象是同一聚合对象的成员,则... 指向具有较大下标值的数组元素的指针比指向同一数组的元素的指针大具有较低的下标值。
这意味着&array[x] <= &array[0]
将 false ,除非x
等于零。
答案 5 :(得分:0)
鉴于遍历数组也可以通过递增指针来实现,后续索引的绝对地址增加似乎是相当基本的。
char[] foobar;
char *foobarPtr = foobar;
foobar[0] == *foobarPtr++;
foobar[1] == *foobarPtr++;
https://www.tutorialspoint.com/cprogramming/c_pointer_to_an_array.htm
答案 6 :(得分:0)
index = &array[x] - &array[0];
是
的语法糖index = (array+x) - (array+0)
因为在C中任何数组都被视为指针。
现在给定pointer arithmetic
,它将被重写为index = x
您可以在ISO9899内谷歌搜索或搜索的相关主题是pointer arithmetic
和desugaring arrays as pointers。