tags:

views:

200

answers:

7

Hi,

I have this loop which gives seg. fault.

  s->c = malloc(width * height * sizeof(double));
  if (s->c == NULL) { puts("malloc failed"); exit(1); }

  for (int n = 0; n < width; n++) {
    for (int m = 0; m < height; m++) {

      d = (&s->c)[m][n];

      printf("d %f\n", d);
      printf("m %i\n", m);
      printf("n %i\n", n);

    }
  }

Inside s->c is:

double* c;

When executed it just outputs:

d 27.000000
m 0
n 0

and then seg. fault.

It worked when I treated the s->c as a 1D array, but I would really like to treat it as a 2D array.

Is that possible, when the c pointer is in a struct?

If so, is (&s->c)[m][n] then the correct way to access the elements?

Sandra

A: 

If you want to allocate s->c as a 1D array, then you can define a macro that does the job for you (but you need to know the second dimension):

#define AR(M, X, Y) ((M)[(Y) + dimy * (X)])
3lectrologos
But in this c++ era, we try to use (inlined) functions instead of macro's.
xtofl
I just didn't want to have to pass the dimension as a parameter as well (so that the macro resembles the c[m][n] expression).
3lectrologos
+4  A: 

I'm very surprised it even compiles. Apparently c is a double*, so (&s->c)[m] is the m'th double. Now, double doesn't have an operator[], so I don't see how the [n] part in (&s->c)[m][n] can be legal.

Presumably, you have declared c differently. There are different solutions: a pointer to a pointer, an pointer to an array of doubles, an array of pointers to doubles, etcetera. All might work, if the allocations match the declaration. In your case, the allocation will not match the declaration.

MSalters
Dave Hinton
How would an "an pointer to an array of doubles" look like?
Sandra Schlichting
double *values[];
tur1ng
@tur1ng; that defines an array of pointers to doubles, not a pointer to an array of double. A pointer to an array of double is declared as `double (*values)[N];`.
John Bode
+2  A: 

The correct way to access the array elements is

d = s->c[m * width + n];

Is that what you mean by treating it as a 1D array?

Dave Hinton
Yes, when I do it like that, it works. But I don't understand why it crashes, when I treat it as a 2D array.
Sandra Schlichting
Because array subscripting is defined in terms of pointer arithmetic. For your code, this means that `(s->c)[m][n]` is evaluated as `*(*(s->c+m)+n)`. This means that `*(s->c + m)` is expected to evaluate as a *pointer* type. However, since `s->c` is type `double *`, `*(s->c + m)` evaluates to a double value. Attempting to treat this double value as a pointer is what leads to the seg fault.
John Bode
+4  A: 

The problem is that the compiler doesn't know the dimensions of your matrix.

When you have: double tab[m][n] you can access the element tab[row][col] as *(tab + (row * n) + col)

In your case you only have double *tab; that can be considered as the pointer to the element tab[0][0] with no information on the matrix dimensions and the compiler can't compute the right address.

You could compute the address yourself (for example using a macro) but would lose the nice tab[x][y] syntax.

I`m surprised it compiles. You should have received at least a warning about implicitly casting a double to a pointer.

Remo.D
+1  A: 

Access the elements using

double d = s->c[m*width+n];

Perhaps through an inline function, to avoid unexpected behaviour.

The compiler does not know about the width of your intended 2D array. It might possibly interpret (&s->c)[m][n] as s->c[m+n], or as something quite different.

Pontus Gagge
+1  A: 

Short answer: you can't treat it as a 2D array, at least not in the way you expect.

The reason writing

(&s->c)[m][n]

doesn't work can be shown as follows. Assume the address of s->c is 0x00080004, and the address of the dynamically allocated memory pointed to by s->c is 0x00001000.

  1. The expression (&s->c)[m][n] is evaluated as *(*(&s->c + m) + n);
  2. The expression &s->c evaluates to 0x00080004;
  3. The expression (&s->c + m) evaluates to 0x00080004+m;
  4. The expression *(&s->c + m) evaluates to the value of whatever is pointed to by 0x00080004+m. If m is 0, then 0x00080004+m points to 0x00001000, which is the address of your dynamically allocated memory (*(&x) == x). If m is any other value, then 0x00080004+m points somewhere random;
  5. The expression (*(&s->c + m) + n) evaluates to whatever 0x00080004+m points to offset by n. If m is 0, then the value is 0x00001000+n, or an offset into your dynamically allocated memory. If m is not 0, then the value is something random;
  6. The expression *(*(&s->c) + m) + n) attempts to dereference the above value. If m is 0, then the result is the value of an element in the dynamically allocated array. If m is not 0, then the result is ... something else. In your case, a segfault.

If you want to dynamically allocate a 2D array, you have to use a pointer to a pointer and allocate it in steps, like so:

struct {
  ...
  double **c;
  ...
} *s;
...
/**
 * Notes:  type of s->c is double **
 *         type of *(s->c) and s->c[i] is double *
 *         type of *(s->c[i]) and s->c[i][j] is double
 */
s->c = malloc(sizeof *(s->c) * rows); 
if (s->c)
{
  for (i = 0; i < rows; i++)
  {
    s->c[i] = malloc(sizeof *(s->c[i]) * columns);
    if (s->c[i])
    {
      // initialize s->c[i][0] through s->c[i][columns-1]
    }
  }
}
John Bode
A: 

I'm surprised nobody mentioned boost::multi_array_ref:

#include <iostream>
#include <boost/multi_array.hpp>

int main()
{
    int rows = 4, cols = 3;

    // Allocate one big block of contiguous data for entire multi-array
    double* arrayData = new double[rows*cols];

    if (arrayData)
    {
        boost::multi_array_ref<double, 2>
             arrayRef(arrayData, boost::extents[rows][cols]);
        for (int row = 0; row < rows; ++row)
        {
            for (int col = 0; col < cols; ++col)
            {
                arrayRef[row][col] = row*cols + col;
                std::cout << arrayRef[row][col] << " ";
            }
            std::cout << std::endl;
        }
    }
    delete [] arrayData;
}

You can also just use boost::multi_array and resize it dynamically:

boost::multi_array_ref<double, 2> marray;
marray.resize(boost::extents[rows][cols]);
Emile Cormier