使用负数作为数组索引

24

我遇到了一个有竞争性的问题,它询问以下代码的输出:

#include <stdio.h>
int main()
{
    int a[] = {0,1,2,3,4};
    int i, *ptr;
    for(ptr = a+4, i=0; i <=4; i++)
    printf("%d", ptr[-i]);
    return 0;
}

我阅读了这个主题:C中是否允许使用负数索引? 但是我不清楚负号如何将数组按相反的顺序生成,即4, 3, 2, 1, 0


9
根据你问题的标题,你没有使用负数作为数组索引;你正在使用指针ptr和索引-i来计算a[]中某个元素的指针运算。数组a[]始终以0为根,并通过a[n]对其进行反引用,其中n小于零是未定义行为。数组不是指针。下面的答案潮解释了指针数学概念。 - WhozCraig
2
a[b] 等同于 *(a + b) 等同于 b[a] - frogatto
记住WhozCraig的上面所提及的答案。在你的职业生涯中,你可能会遇到使用这种指针来“poke”和“zap”字节数组中的值的代码。有时甚至会用它来强制终止字符串?以上问题具有“Python风格”,因为在Python中可以使用否定形式? - mckenzm
6个回答

26

首先,回想一下在C语言中,表达式ptr [index]的意思与*(ptr + index)是相同的。

现在让我们再次看一下您的表达式:ptr在循环之前设置为a+4; 然后你对它应用了一个负的i索引。因此,等价的指针算术表达式如下:

printf("%d", *(a+4-i));

这个表达式反向迭代数组,产生您看到的结果。


5
简洁而优美的解释! - Jishan

4
这个方法可行的原因是[]运算符进行了指针加法。
当您引用时
a[x]

实际上发生的是,它获取变量a的内存地址并加上sizeof(int)*x。
因此,如果你将ptr设置为a+4,则会变成a+sizeof(int)*4。
当输入负数时,会向内存地址后移动。

3
在for语句中使用in
for(ptr = a+4, i=0; i <=4; i++)

指针ptr被设置为a+4。也可以以下面的方式完成

ptr = &a[4];

如果您尝试输出指针所指向的值,例如:
printf( "%d\n", *ptr );

你将得到4。这是指针指向数组的最后一个元素。

循环中使用了表达式ptr[-i]。当i等于0时,它相当于ptr [0]或简单地写成*ptr,输出的是数组的最后一个元素。 当i等于1时,表达式ptr [-i]等同于a [4-1]或简单地写成a[3]。当i等于2时,表达式ptr[-i]等同于a [4-i]a [4-2],而a[4-2]又等于a [2],以此类推。 所以你会得到

4321

3
a+4 会给出指向 a 的第五个元素的指针。因此,ptr 指向那个位置。
然后循环计数器 i 从0递增到(包括)4。
解引用运算符 ptr[-i] 等同于 *(ptr - i) (根据定义)。因此,由于 i 是0,ptra+4 ,它等同于 a+4-0,然后是 a+4-1,再然后是 a+4-2,一直到 a+4-4,也就是显而易见的等于 a

3
ptr[-i]会衰减为*(ptr + (-i))。在第一次迭代时,当i = 0时,ptr[-i]访问a数组的最后一个元素,因为最初ptr被设置为等于a + 4,这意味着-取a的开始地址并加上4 * sizeof(int)(因为ptr的大小为int)。在每个下一次迭代中,当i增加时,将访问数组的前一个元素。

严格来说,表达式一并不会“衰变”成表达式二:它们只是等价的表达式。然而,如果你按照 OP 对 a 的定义将 *(a + (-i)) 写成这样,那么 a 确实会衰变为指向 a 的第一个元素的指针。 - Peter - Reinstate Monica

2

正如我在C/C++的评论中提到的那样

a[b] == *(a+b) == b[a]

对于您的情况,这些都是可以的。
printf("%d", *(a + 4 - i));
printf("%d", a[4 - i]);
printf("%d", 4[a - i]);
...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接