指向[-1]数组索引的指针

时间:2010-03-02 09:07:28

标签: c++ c arrays pointers

指针如何指向[-1]数组的索引每次产生合法输出。指针赋值实际发生了什么?

#include<stdio.h>
int main()
{
        int realarray[10];
        int *array = &realarray[-1];

        printf("%p\n", (void *)array);
        return 0;
}

代码输出:

manav@workstation:~/knr$ gcc -Wall -pedantic ptr.c
manav@workstation:~/knr$ ./a.out
0xbf841140

编辑:如果这种情况有效,那么我可以用它来定义索引从1开始而不是0的数组,即:array [1],array [2],..

12 个答案:

答案 0 :(得分:13)

您只需获取一个包含该“虚构”位置地址的指针,即第一个元素&realarray[0]的位置减去一个元素的大小。

这是undefined behavior,如果您的机器具有分段内存架构,可能会破坏性。这是有效的,因为编译器编写者选择实现上面概述的算法;随时都可能发生变化,而另一个编译器可能表现完全不同。

答案 1 :(得分:9)

a[b]定义为*(a+b)

因此a[-1]*(a-1)

a-1是否是有效指针,因此取消引用有效取决于使用代码的上下文。

答案 2 :(得分:3)

行为未定义。

您所观察到的内容可能发生在您的特定编译器和配置中,但任何情况都可能发生在不同的情况。你完全不能依赖这种行为。

答案 3 :(得分:3)

行为未定义。您只能计算指向数组中任何元素的指针,或者只能计算一个指针,但就是这样。您只能取消引用指向数组中任何元素的指针(而不是指针的过去)。查看您的变量名称,看起来就像是在问this C FAQ的问题。我认为FAQ的答案非常好。

答案 4 :(得分:3)

虽然正如其他人所说,在这种情况下它是未定义的行为,但它会在没有警告的情况下编译,因为一般foo[-1]可能有效。

例如,这很好:

int realarray[10] = { 10, 20, 30, 40 };
int *array = &realarray[2];

printf("%d\n", array[-1]);

答案 5 :(得分:2)

在C和C ++中,不会在运行时检查数组索引。您正在执行指针运算,这可能会或可能不会最终给出定义的结果(不在此处)。

但是,在C ++中,您可以使用提供边界检查的数组类,例如boost::arraystd::tr1::array(将被添加到C ++ 0x中的标准库中):

#include <cstdio>
#include <boost/array.hpp>

int main()
{
    try {
        boost::array<int, 10> realarray;
        int* p =  &realarray.at(-1);
        printf("%p\n", (void *)p);
    } catch (const std::exception& e) {
        puts(e.what());
    }
}

输出:

  

array&lt;&gt ;: index超出范围

还会产生编译器警告:

  

8 test.cpp [警告]传递否定   价值-0x000000001' for converting 1 of T&amp;升压::阵列::在(为size_t)   [使用T = int,unsigned int N = 10u]'

答案 6 :(得分:1)

它只是指向内存中数组前面的项目地址。

可以简单地将数组视为指针。然后简单地将其减1。

答案 7 :(得分:1)

  

这里你只是执行指针算术,它将获得relarray的第一个索引地址

     

请注意,如果你和&amp; relarray [+1],你将得到数组的第二个元素地址。从那以后

     

&amp; relarray [0]指向第一个索引地址。

答案 8 :(得分:0)

array指向realarray起始地址之前的一个位置。然而,令我感到困惑的是为什么在没有任何警告的情况下进行编译。

答案 9 :(得分:0)

你只是指向阵列前面的4个字节。

答案 10 :(得分:0)

这是完全明确的定义。您的代码保证被所有编译器接受,并且永远不会在运行时崩溃。 C / C ++指针是一种符合算术规则的数字数据类型。加法和减法工作,括号表示法[]只是一种奇特的加法语法。 NULL实际上是整数0。

这就是C / C ++危险的原因。编译器将允许您创建指向任何地方的指针而无需投诉。 解除引用示例中的野生指针,*array = 1234;会产生未定义的行为,从细微的损坏到崩溃。

是的,您可以使用它从1开始索引。不要这样做! C / C ++习语总是从0开始索引。其他看到代码索引从1开始的人会试图“修复”它从0开始索引。

答案 11 :(得分:0)

实验可以提供更多线索,如果是以下内容。而不是将指针值打印为

printf("%p\n", (void *)array);

,打印数组元素值

printf("%d\n", *array);

多数民众赞成因为打印带有%p的指针总会产生一些输出(没有任何不当行为),但是没有任何内容可以从中推断出来。