是否可以使用负索引访问二维数组?

Is it possible to access two dimensional array with negative indexing?

我正在尝试访问程序中的二维数组,而且我正在尝试使用负索引(它有助于我的思维步骤)。我想使用最简洁的语法来访问数组元素,即 a[i][j].

然而,当我 运行 程序时,出现分段错误。

#include <iostream>

int main (void)
{
    int i,j;
    int arr[3][3];
    int ** p;

    for( i=0; i<3; i++)
    {
        for(j=0; j<3; j++)
        {
            arr[i][j] = i+j;
        }
    }

    p = (int **)&(arr[1][1]);

    for( i=-1; i<2; i++)
        {
            for(j=-1; j<2; j++)
            {
                std::cout << p[i][j] << std::endl;
            }
        }
    return 0;
}

我不想使用 p[i*something + j] 之类的东西来访问数组元素。可能吗?

C++标准定义:

5.2.1/1 (...) unscoped enumeration or integral type. (...) The expression E1[E2] is identical (by definition) to *((E1)+(E2))

所以不需要正积分(不像数组的大小,根据 8.3.4 必须是正的)。 5.7 定义了 E1 + E2,当 E1 是指针,E2 是正整数或负整数时。

所以是的,原则上它应该是有效的。示例:

 int a[4][5] = { { 11, 12, 13, 14,15 }, { 21, 22, 23, 24,25 }, { 31, 32, 33, 34,35 }, { 41, 42, 43, 44,45 } };
 typedef int (*myarr)[5];
 myarr p  = (myarr) &a[2][2];
 cout << p[0][0] << endl << p[-2][-2]<<endl; 

但这不是一个好的做法,因为:

  • 这可能会造成混淆,因为大多数人都希望指数为正
  • std::vectorsstd::array 都用 无符号整数 类型定义了 operator[]。所以你的负索引实践不能轻易转换为更高级的数据结构
  • 它适用于多维数组(由于数据的连续性),但它根本不适用于数组的数组,例如 int**.

负索引可以用在数组下标中,[]中的索引基本上是一个指针的偏移量,所以如果你写:

 int array[5]={1,2,3,4,5};
 int *arr=&array[2];
 int val=arr[-2];

你得到 1 个。

据我了解,您要实现的是给定二维数组中的一个位置(代码中的 [1][1]),您希望对该位置的邻域执行一些操作。以下是一种可读的方法,我强烈建议这样做:

int row = 1;
int col = 1;
for (int i = -1; i < 2; i++) {
    for (int j = -1; j < 2; j++) {
        std::cout << arr[row + i][col + j] << std::endl;
    }
}

不过如果你真的想弄乱指针,还是有办法的。您可以将 &arr[1][1] 视为指向长度为 3 的数组(arr 的最后一个维度)的指针,这样您就可以做到这一点:

static cont int ROW_LEN = 3; // 3 here is the last dimension of arr
typedef int (*arrRowPtr)[ROW_LEN]; 
arrRowPtr p = (arrRowPtr)&arr[1][1];

for (int i = -1; i < 2; i++) {
    for (int j = -1; j < 2; j++) {
        std::cout << p[i][j] << std::endl;
    }
}

为什么起作用:p[i]*(p + i)是一样的,pi就是加i * ROW_LEN * sizeof(int),也就是移动位置按 i 行(向前或向后),保留列。 *(p + i) 的类型是 int[ROW_LEN],为了指向 int* 的指针运算而衰减,这意味着 p[i][j] 会将 j * sizeof(int) 添加到 p[i] ,即按 j.

更改列

这有效(稍后我会更深入地解释):

int (*p)[3] = (int (*)[3])((int *)&arr + 4);

首先。表达式 (int *)&arr + 4 形成指向与 &arr[1][1] 相同位置的指针。

然而,我选择了第一种形式,以避免可能的反对意见,即 &arr[1][1] 形成一个指针,该指针不能用于访问子数组 arr[1] 的边界之外。在 C 中这是一个有效的反对意见,而在 C++ 中则不太清楚。 C++ 标准在这方面使用不同的措辞。无论如何,我通过在单个对象 arr.

中使用偏移量来完全避免该主题

在那之后,它被转换成一个指向由 3 个整数组成的数组的指针。 (即与 &arr[0] 相同的类型)。

我认为可以使用它来访问 arr 中的任何 int,尽管我还没有看到很多关于使用指向数组的指针的讨论它与有效 space 和无效 space 重叠。 (我要 post 关于那个的新问题...)

没有容易的方法来做到这一点。但你可以通过一些工作来做到这一点。基本上你所要做的就是获取指向数组的指针,然后将它们移动到你想要零位置的任何位置。

#include <assert.h>
#include <stdio.h>

int main(int argc, char* argv[]) {

    const int minIndex = -5;
    const int maxIndex = 5;
    const int size = maxIndex - minIndex;

    assert(minIndex < maxIndex);

    int arr[size][size];

    // initialize the values in the array
    for (int i = 0; i < size; ++i) {
        for (int j = 0; j < size; ++j) {
            arr[i][j] = size*i + j;
        }
    }

    // first get the shifted versions of the inner arrays
    int* shiftedInner[10]; 
    for (int i = 0; i < size; ++i) {
        shiftedInner[i] = arr[i] - minIndex;
    }
    // then shift the outer one 
    int** shifted = &shiftedInner[-minIndex];


    // and now you can work with it as if the
    // base array were arranged like you want it
    printf("%d", shifted[minIndex][minIndex]);
    printf("%d", shifted[-3][-4]);
}

这很恶心,我不建议在生产代码中这样做。但这是可能的。