...仅将指针递减到分配的范围之外对我来说似乎是很粗略的。这是C中的“允许”行为吗?
允许吗 是。好主意?通常不行。
C是汇编语言的缩写,在汇编语言中没有指针,只有内存地址。C的指针是内存地址,具有在执行算术时会根据其指向的大小递增或递减的副作用。从语法的角度来看,这使以下内容很好:
double *p = (double *)0xdeadbeef;
--p; // p == 0xdeadbee7, assuming sizeof(double) == 8.
double d = p[0];
在C中,数组并不是真正的东西。它们只是指向类似于数组的连续内存范围的指针。该[]
运营商是做指针运算和非关联化,所以简写a[x]
实际手段*(a + x)
。
进行上述操作是有正当理由的,例如某些I / O设备将几个double
s映射到0xdeadbee7
和中0xdeadbeef
。很少有程序需要这样做。
当您创建某物的地址时,例如通过使用&
运算符或调用malloc()
,您希望保持原始指针不变,以便您知道它指向的内容实际上是有效的。减少指针意味着一些错误的代码可能会尝试对其进行取消引用,从而获得错误的结果,破坏某些内容,或者根据您的环境而违反分段要求。对于尤其如此malloc()
,因为您将负担加在了free()
要记住传递原始值的任何人身上,而不是使所有内容崩溃的更改版本。
如果您需要使用C中的基于1的数组,则可以安全地执行此操作,但要分配一个永远不会使用的其他元素:
double *array_create(size_t size) {
// Wasting one element, so don't allow it to be full-sized
assert(size < SIZE_MAX);
return malloc((size+1) * sizeof(double));
}
inline double array_index(double *array, size_t index) {
assert(array != NULL);
assert(index >= 1); // This is a 1-based array
return array[index];
}
请注意,这并不能防止超出上限,但这很容易处理。
附录:
C99草案的一些章节(很抱歉,我只能链接到这本书):
§6.5.2.1.1说,与下标运算符一起使用的第二个(“其他”)表达式是整数类型。 -1
是一个整数,它p[-1]
有效,因此也使指针&(p[-1])
有效。这并不意味着访问该位置的内存将产生已定义的行为,但是指针仍然是有效的指针。
第6.5.2.2节说,数组下标运算符的求值等同于将元素编号加到指针,因此p[-1]
等效于*(p + (-1))
。仍然有效,但可能不会产生期望的行为。
§6.5.6.8说(强调我):
将具有整数类型的表达式添加到指针或从指针中减去时,结果将具有指针操作数的类型。
...如果表达式P
指向i
数组对象的-th元素,则表达式(P)+N
(等效为N+(P)
)和(P)-N
(其中N
值为n
)分别指向数组对象的i+n
-th和
i−n
-th元素(如果存在)。
这意味着指针算术的结果必须指向数组中的元素。并不是说算术必须一次完成。因此:
double a[20];
// This points to element 9 of a; behavior is defined.
double d = a[-1 + 10];
double *p = a - 1; // This is just a pointer. No dereferencing.
double e = p[0]; // Does not point at any element of a; behavior is undefined.
double f = p[1]; // Points at element 0 of a; behavior is defined.
我是否建议以这种方式做事?我没有,我的回答解释了原因。