1

I have a 2d array defined as double mat[6][6]. To let gcc vectorize the for I need to use a single loop. I have the following loop:

double* p = &mat[0][0];
for (int i = 0; i < 36; i++)
     sum += p[i] + 48;

I have the warning from gcc saying: array subscript is above array bounds. Why?

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
greywolf82
  • 21,813
  • 18
  • 54
  • 108
  • Are you sure that GCC *can't* vectorize nested loops? – Caleth Mar 12 '18 at 13:18
  • gcc probably recognizes that the inner array has 6 elements only. (Not that I think it shouldn't work.) About consecutive storage of multi-dim array elements (and whether row alignment may or may not occur), I once read a hard discussion but I cannot remember the actual topic anymore. (Too sad.) OT: `sum = ` -> `sum +=`? – Scheff's Cat Mar 12 '18 at 13:19
  • Yes, I tried and it doesn't work, I'm using GCC 4.4.1 and I can't upgrade – greywolf82 Mar 12 '18 at 13:19
  • 1
    duplicate? https://stackoverflow.com/questions/7269099/may-i-treat-a-2d-array-as-a-contiguous-1d-array – UKMonkey Mar 12 '18 at 13:20
  • Note: while the highest voted answer there says "yes it's valid to do so"; the highest voted comment states it's UB... but the comment at least has a quote from the standard – UKMonkey Mar 12 '18 at 13:22
  • I cant reproduce, which compiler version and options are you using? https://wandbox.org/permlink/FIaN6z8WqFK3J97j – Amadeus Mar 12 '18 at 13:23
  • 1
    There was just a answer last week stating this was UB with some good standard quotes. I'm trying to find it now. – NathanOliver Mar 12 '18 at 13:24
  • 1
    For a similar case, I once used `union { double mat[4][4]; double values[16]; };` (May be, some `assert()`s around to let me sleep better.) – Scheff's Cat Mar 12 '18 at 13:24
  • Here we go: https://stackoverflow.com/questions/49174008/access-to-elements-of-array-of-arrays-using-common-iterator/49174320#49174320 – NathanOliver Mar 12 '18 at 13:25
  • @Scheff Interestring workaround – greywolf82 Mar 12 '18 at 13:26
  • Beside of the fact, that it works (currently with VS2013), I'm really not sure if this is correct in general. Just found [SO: Unions and type-punning](https://stackoverflow.com/a/25672839/7478597) - hence, the `assert()`s. – Scheff's Cat Mar 12 '18 at 13:36
  • 2
    @greywolf82: Although that's UB too - you can't type pun through a `union` in C++. – Bathsheba Mar 12 '18 at 13:36
  • @Bathsheba I see two possible dangers: 1. `sizeof(double[4][4]) != sizeof(double[16])` hard to believe and simple to test. 2. Some optimization in code (re-ordering, caching, ...) prevents that reading from `values[15]` doesn't return what has been written to `mat[3][3]`. This is what concerns me really. Beside of the fact, that aggressive optimization which may cause this will probably occur in release only but not in debugging where you could at least observe this easily... – Scheff's Cat Mar 12 '18 at 13:44
  • @Scheff: Personally I use BLAS from Boost for all matrix classes. Then I get to go home early. The code as presented above is UB - out of bounds array access. An array of arrays is not the same as an array. That's pretty much it. – Bathsheba Mar 12 '18 at 13:47
  • 3
    The most simple and robust solution is probably to stick to 1d array for storage. 2D access might be done by a little function which does the div/mod. (or encapsulation in a class to glue all together.) – Scheff's Cat Mar 12 '18 at 13:48
  • @Scheff Yep, I'm quite agree, better at this point to use a single 1d array accessing with [i*NCOLS+j] when needed to use it like a 2d array – greywolf82 Mar 12 '18 at 13:49

3 Answers3

2

Not directly answering your question, but as an alternative you can determine the indices for the individual dimensions (so long as it's two dimensional) using mod and div like so:

int height = 6;
int width = 6;

for (int i = 0; i < 36; i++)
{
     sum = mat[i / height][i % width] + 48;
}
LordWilmore
  • 2,829
  • 2
  • 25
  • 30
1

The best bet would be to use pointers. Since you want to use a single loop, it would require using a different logic. Posting the answer first:

double* p = &mat[0][0];
for (int i = 0; i < 36; i++)
     sum += *(*(p+ int(i/6)) + i%6)+ 48;

Pointers for 2D arrays work in the following manner. Consider a int matrix[3][3] :

matrix               =>    Points to base address of two-dimensional array.
                           Since array decays to pointer.

*(matrix)            =>    Points to first row of two-dimensional array.
*(matrix + 0)        =>    Points to first row of two-dimensional array.
*(matrix + 1)        =>    Points to second row of two-dimensional array.

**matrix             =>    Points to matrix[0][0]
*(*(matrix + 0))     =>    Points to matrix[0][0]
*(*(matrix + 0) + 0) =>    Points to matrix[0][0]
*(*matrix + 1)       =>    Points to matrix[0][1]
*(*(matrix + 0) + 1) =>    Points to matrix[0][1]
*(*(matrix + 2) + 2) =>    Points to matrix[2][2]

So the code works as follows:

*(*(p+ int(i/6)) + i%6) on i=0 gives *(*(p+ 0) + 0)

*(*(p+ int(i/6)) + i%6) on i=1 gives *(*(p+ 0) + 1)

*(*(p+ int(i/6)) + i%6) on i=2 gives *(*(p+ 0) + 2)

and the loop goes upto i=35.

Shivansh Jagga
  • 1,541
  • 1
  • 15
  • 24
0

Not answering your question, but you could parse your mat this way:

for(int i=0, j=0; i < 6; (++j < 6) ? (j):(++i,j=0))
{
  sum += mat[i][j] + 48;
}
Churam
  • 1
  • 2