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

Posted

技术标签:

【中文标题】是否可以使用负索引访问二维数组?【英文标题】:Is it possible to access two dimensional array with negative indexing? 【发布时间】:2015-03-24 20:37:47 【问题描述】:

我正在尝试访问程序中的二维数组,并且我正在尝试使用负索引(它有助于我的心理步骤)。我想使用最简洁的语法来访问数组元素,即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] 之类的东西来访问数组元素。有可能吗?

【问题讨论】:

现在你有一个更大的问题。 int**int[x][y] 等效。我会先担心这个。您收到的错误并因此被那个可怕的演员掩盖并不是为了给您带来不便。它试图告诉你一些事情。 @WhozCraig p 的正确类型应该是什么,这样它才能发挥作用?程序编译成功。 你的意思是int (*p)[3] = (int (*)[3])((int *)&amp;arr + 4);。之后我认为进行索引是合法的,尽管不完全确定 您总是可以编写自己的二维向量类,它使用负索引。但我更愿意建议您习惯基于 0 的索引。 @TheParamagneticCroissant &amp;arr + 4 远远超出了数组的末端 【参考方案1】:

这行得通(我稍后会更深入地解释):

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

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

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

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

我认为使用它访问arr 内的任何int 是可以的,尽管我还没有看到很多关于使用指向数组的指针的讨论,因为它与有效空间和无效空间重叠空间。 (我将就此发布一个新问题......)

【讨论】:

【参考方案2】:

据我了解,您要实现的是给定二维数组中的位置(代码中的[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;
    

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

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) 相同,将i 添加到p 意味着添加i * ROW_LEN * sizeof(int),即将位置移动i 行(向前或向后),保留列。 *(p + i)的类型是int[ROW_LEN],为了指针算术而衰减到int*,这意味着p[i][j]会将j * sizeof(int)添加到p[i],也就是将列更改为j

【讨论】:

【参考方案3】:

C++ 标准定义:

5.2.1/1 (...) 无作用域枚举或整数类型。 (...) 表达式 E1[E2] 与 *((E1)+(E2)) 相同(根据定义)

因此,它不需要具有正积分(与根据 8.3.4 必须为正的数组的大小不同)。当 E1 是指针并且 E2 是正整数或负整数时,5.7 定义了 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**

【讨论】:

【参考方案4】:

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

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

你得到 1。

【讨论】:

二维数组怎么做?【参考方案5】:

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

#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]);

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

【讨论】:

以上是关于是否可以使用负索引访问二维数组?的主要内容,如果未能解决你的问题,请参考以下文章

Java数组2----二维数组

Java数组2----二维数组

C#从函数返回二维数组索引

Handlebars.js - 从二维数组访问父索引

Java 中的二维数组

FLASH AS3 二维数组如何查找某个元素的索引?