0

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?

bubble
  • 3,408
  • 5
  • 29
  • 51
  • 4
    Right now you have a much bigger problem. `int**` and `int[x][y]` are **not** equivalent. I'd worry about that first. The error you received and hence covered up with that hideous cast wasn't there for inconvenience. It was trying to tell you something. – WhozCraig Mar 24 '15 at 20:40
  • 1
    @WhozCraig what should be correct type for `p` so that it can fulfill its role? The program compiled successfully. – bubble Mar 24 '15 at 20:42
  • @bubble `&(arr[1][1])` is `int*`. – Anton Savin Mar 24 '15 at 20:48
  • @VladfromMoscow Was trying various mutations of the code to get it to work. Corrected now. Thanks for pointing. – bubble Mar 24 '15 at 20:49
  • 1
    You meant `int (*p)[3] = (int (*)[3])((int *)&arr + 4);` . After which I think it is legal to do your indexing although not entirely sure – M.M Mar 24 '15 at 20:50
  • 1
    You could always write you own 2-dimensional vector class, that uses negative indexing. But I would rather recommend getting used to 0-based indexing. – Viktoria Andersson Mar 24 '15 at 20:51
  • 1
    @TheParamagneticCroissant `&arr + 4` is way beyond the ends of the array – M.M Mar 24 '15 at 20:51
  • @MattMcNabb yeah, that's why it's UB. By the way, re-alising through `int *` to a pointer which is out of bounds of a subobject is also undefined behavior. – The Paramagnetic Croissant Mar 24 '15 at 20:52
  • @TheParamagneticCroissant `(int *)&arr + 4` is quite different to `&arr + 4` – M.M Mar 24 '15 at 20:56
  • @MattMcNabb I'm totally aware of that. If you have an array of arrays of 3 elements, then `(int *)&arr + 4` is equally invalid. – The Paramagnetic Croissant Mar 24 '15 at 20:57
  • This array has 9 `int`s in it . `(int *)&arr + 4` gives the same address as `&arr[1][1]` – M.M Mar 24 '15 at 20:58
  • @MattMcNabb nope, it's undefined behavior, again, because it's an out-of-bounds pointer to the subobject. [answer 1](https://stackoverflow.com/questions/6015080/c-c-is-this-undefined-behavior-2d-arrays), [answer 2](https://stackoverflow.com/questions/25139579/2d-array-indexing-undefined-behavior), [answer 3](https://stackoverflow.com/questions/8428605/can-i-access-multidimensional-array-using-a-pointer). – The Paramagnetic Croissant Mar 24 '15 at 21:01
  • @MattMcNabb your solution worked perfectly. Although it will take me quite sometime to figure out how it worked. Thanks for help. – bubble Mar 24 '15 at 21:02
  • @bubble it didn't "work"; it only *pretended/happened* to work. Matt is unfortunately not aware that his "solution" invokes undefined behavior and it is illegal to do what he suggested. – The Paramagnetic Croissant Mar 24 '15 at 21:03
  • @TheParamagneticCroissant my idea doesn't invoke any of the UBs that you link to – M.M Mar 24 '15 at 21:04
  • @MattMcNabb well then what does it do? You suggested `(int *)&arr + 4` which is exactly what all three answers are about. – The Paramagnetic Croissant Mar 24 '15 at 21:05
  • @TheParamagneticCroissant none of those answers use that construct. It is very different to `&arr[0][4]` . – M.M Mar 24 '15 at 21:08
  • 1
    Don't fight the language; learn to embrace it :) – Julian Mar 24 '15 at 21:08
  • @MattMcNabb the syntactic construct is different, but the *problem* is the same. You are making a pointer point to a subobject which is not there. End of story. – The Paramagnetic Croissant Mar 24 '15 at 21:09
  • @TheParamagneticCroissant rubbish, there are 9 contiguous ints inside `arr`. `(int *)&arr + N` can be used to select any of them. I can't fathom how anyone can possibly argue against it. Would you also say that `(char *)&arr + N * sizeof(int)` only works for N is 0-2 in this case? – M.M Mar 24 '15 at 21:12
  • 1
    Why so many negative votes? I don't think the answer is too obvious. – bubble Mar 24 '15 at 21:13
  • @MattMcNabb Yes, there are 9 contiguous `int`s, but the subarrays only contain 3 immediate subobjects: 3 `int`s each. You are converting the address of the array, which is – when suitably converted – the same as address of the first subarray, to a pointer-to-`int`. Hence, this can be used to access `arr[0][0…2]`, but that's all. The array being contiguous in memory doesn't help, because it's unrelated to the fact that the first subarray has only 3 elements. – The Paramagnetic Croissant Mar 24 '15 at 21:22
  • Ignoring all the other reasons not to do this, where would your arrays begin and end? When you declare `int a[3];`, what are the valid indices? Is accessing `a[-1]` valid? How about `a[-2]`? Does that make `a[0]` the last element in the array? Or do all of your arrays now go from `-1` to `size-2`? Things get messy when you deviate from zero (or one, in some languages)-based indexing. – Julian Mar 24 '15 at 21:23
  • @TheParamagneticCroissant By that logic you can't use `char *` to iterate over `arr`; would you agree? – M.M Mar 24 '15 at 21:23
  • @MattMcNabb I don't see how that is relevant at all; using a `char *` to iterate over the array would mean to inspect its byte-wise representation. Any object is allowed to have its contents read through a `char *` (that's an explicit exception added to the strict aliasing rule), but it has nothing to do with reading through a pointer to compatible type. – The Paramagnetic Croissant Mar 24 '15 at 21:25
  • @TheParamagneticCroissant what difference is there between using `char *` and `int *` as the iterator type? There is no strict aliasing violation because the pointer to `int` is used to access objects of type `int`. (An array contains objects of the element type of the array, and so on recursively for multi-dimensional arrays). – M.M Mar 24 '15 at 21:27
  • @AtlasC1 I think it's a matter of choice. Negative indexing makes my long calculations more readable and hence less error prone if I am able to compare the code directly to a text book formula. – bubble Mar 24 '15 at 21:28

5 Answers5

1

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**.
Christophe
  • 68,716
  • 7
  • 72
  • 138
1

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.

Anton Savin
  • 40,838
  • 8
  • 54
  • 90
1

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...)

M.M
  • 138,810
  • 21
  • 208
  • 365
0

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.

riodoro1
  • 1,246
  • 7
  • 14
0

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.

ahruss
  • 2,070
  • 16
  • 21