2013-06-18 66 views
7

我正在阅读K & R中的数组算术部分,并且发现了一些好奇的东西。我发布了整个段落的背景,但我主要集中在大胆的部分。用于数组的C指针算术

如果p和q指向同一阵列的成员,然后像==, !=,<关系,> =,等等,正常工作。例如,p < q如果p指向 而不是q的数组的早期成员,则q为真。任何指针可以是 有意义地与零相等或不平等进行比较。但 行为未定义算术或与指针 不指向同一数组的成员的指针。 (有一个例外: 过去的阵列的端部的第一个元素的地址可在指针运算使用 。)

什么是该异常的原因是什么?在定义大小时,是否将多余的内存分配给任何数组的末尾?如果是这样,为了什么目的?它是用空字符结束数组吗?

+1

理解这个最简单的方法是:a [b] = a + b。看第98页的解释 – SheetJS

+2

@Nirk:嗯?这与这有什么关系? – jason

回答

8

的原因是这样你就可以在这样的循环递增指针:

char a[42], *p; 

for (p = a; p < &a[sizeof a]; p++) // or p != &a[sizeof a] 
{ 
    /* ... */ 
} 

如果没有额外的规则,这将是不确定的行为,因为该指针将是无效的。

0

在数组末尾没有分配额外的内存。它只是说你可以在指针算术中使用'End'标记的地址。开始指向数组的第一个元素。终点指向的第一个元素过去数组的末尾。

----------------- 
| | | | | 
----------------- 
^    ^
Begin   End 
4

是被定义它们的大小当分配给任何阵列的端部的多余碎片存储器?

不是。您引用的上下文很重要。你粗体的例外是参考指针算术(和关系)。这是说,如果你做指针之间的指针关系而不是指向同一个数组的成员,那么你得到udb。但是,有一个例外,就是如果指针中的任何一个都指向数组末尾的第一个元素。

如果是这样,为了什么目的?

null答案,因为它假设一个错误的前提。

是否以空字符结束数组?

这样做的原因是,使得相比于数组的末尾是合法的号,也就是比较来&a[sizeof a]a是一个数组。请注意,&a[sizeof a]是数组末尾的第一个元素。如果p是指向a元素的指针或者也是数组末尾之后的第一个元素,则可以将p&a[sizeof a]进行比较。

我引自C99 specification,第6.5.8.5节。

当比较两个指针时,结果取决于指向对象的地址空间中的相对位置。如果指向对象或不完整类型的两个指针都指向同一个对象,或者两个指向同一个数组对象的最后一个元素,则它们相等。如果指向的对象是同一个聚合对象的成员,那么稍后声明的结构成员的指针比结构中较早声明的成员的指针要多,指向具有较大下标值的数组元素的指针比指向同一数组元素的指针大具有较低的下标值。如果表达式P指向数组对象的元素,并且表达式Q指向同一数组对象的最后一个元素,则指针表达式Q + 1的比较结果大于P。在所有其他情况下,行为是不确定的。

0

您只需计算一个数组尾部的对象的地址,并承诺您不会因此而陷入困境。您不允许取消引用该指针。

这个承诺很重要的一个例子是,一个对象可能被分配在内存的最后,所以当你计算出地址时,一个结束的地址会导致算术溢出。如果要迭代指针通过该数组,则在最后一次迭代之后,算术溢出可能会导致指针环绕并指向NULL。

这可能会导致比较结果被倒置,并且它可能会使用数组边界检查器跳动各种警报铃声,或者如果CPU使用例如饱和算法,它可能会简单地计算错误的地址。

因此,编译器和链接器有责任确保不会发生这种情况,程序员有责任确保编译器和链接器将责任限制在一个简单的情况下,当你在末尾运行n元素时,你必须坚持相同的保证。