bubble
bubble

Reputation: 3526

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

I am trying to access a two dimensional array in a program and also I am trying to use negative indexes (it helps me in mental steps). I wanted to use the neatest possible syntax for access array element viz a[i][j].

However when I run the program , I get segmentation fault.

#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;
}

I don't want to use something like p[i*something + j] for accessing array elements. Is it possible?

Upvotes: 0

Views: 1523

Answers (5)

Christophe
Christophe

Reputation: 73376

C++ standard defines:

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

So it's not required to have postive integrals (unlike the size of an array which must be positive according to 8.3.4). And 5.7 defines E1 + E2 when E1 is a pointer and E2 is a positive or negative integral.

So yes, in principle it should be valid. Example:

 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; 

However it's not a good practice, because:

  • it may create confusion as most people expect index being positive
  • std::vectorsand std::array both have operator[] defined with an unsigned integral type. So your negative indexing practice could not be easily converted to more advanced data structures
  • it works for multidimensional arrays (thanks to contiguity of data), but it doesn't work at all with arrays of arrays such as int**.

Upvotes: 1

Anton Savin
Anton Savin

Reputation: 41301

As I understand, what you are trying to achieve is that given a position in a 2D array ([1][1] in your code), you want to perform some operations for the neighbourhood of that position. One of readable ways to do it is the following, and I strongly advise it:

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;
    }
}

Though if you really want to mess with pointers, there is a way. You can treat &arr[1][1] as a pointer to an array of length 3 (the last dimension of arr), and it will allow you to do the trick:

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;
    }
}

Why it works: p[i] is the same as *(p + i), and adding i to p means adding i * ROW_LEN * sizeof(int), that is moving the position by i rows (forward or backward), preserving the column. The type of *(p + i) is int[ROW_LEN], decaying for the purpose of pointer arithmetic to int*, which means that p[i][j] will add j * sizeof(int) to p[i], that is change the column by j.

Upvotes: 1

ahruss
ahruss

Reputation: 2130

There's no easy way to do this. But you could do it with some work. Basically all you have to do is get pointers to the array and then move them to wherever you want the zero position to be.

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

This is gross and I don't recommend doing it in production code. But it is possible.

Upvotes: 0

M.M
M.M

Reputation: 141586

This works (I'll explain in more depth in a moment):

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

First of all. The expression (int *)&arr + 4 forms a pointer to the same location as &arr[1][1].

However I have chosen the first form to avoid the possible objection that &arr[1][1] forms a pointer which may not be used to access outside of the bounds of the sub-array arr[1]. In C that is a valid objection, and in C++ it is less clear. The C++ standard uses different wording in this area. In any case, I avoid the topic entirely by using an offset into the single object arr.

After that, it is cast as if it is a pointer to an array of arrays of 3 ints. (i.e. the same type as &arr[0]).

I think it is OK to use this to access any int which is inside arr, although I haven't seen a lot of discussion surrounding the use of pointer-to-array when it overlaps valid space and invalid space. (Am going to post a new question on that...)

Upvotes: 1

riodoro1
riodoro1

Reputation: 1256

Negative indices can be used in array subscripts, the index in [] is basically an offset from a pointer so if You write:

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

You get 1.

Upvotes: 0

Related Questions