views:

156

answers:

5

Hi all,

I have been trying to figure out the problem with my allocation and use of a multidimensional dynamically allocated array in C. I'd really appreciate any help.

I've tried two approaches. The first:

cdr = (double ***) malloc(NUM_REGIONS * sizeof(double **));
for(i=0; i<NUM_REGIONS; i++){
   cdr[i] = (double **) malloc(numRatings * sizeof(double *));
   for(j=0; j<numRatings; j++){
       cdr[i][j] = (double *) malloc(remQuarters * sizeof(double));
   }
}  

And the second:

tempPtr1 = (double *) malloc(NUM_REGIONS * numRatings * remQuarters * sizeof(double) );
tempPtr2 = (double **) malloc (NUM_REGIONS * numRatings * sizeof(double *));
cdr = (double ***) malloc(NUM_REGIONS * sizeof(double **));
for(i=0; i< NUM_REGIONS; i++){
    cdr[i] = tempPtr2 + i;
    for(j=0; j < numRatings; j++) cdr[i][j] = tempPtr1 + i * NUM_REGIONS + j;
}

Neither is working. In both cases, each cdr[i] ends up pointing to the same place. The first time I step into the 'i' loop, all cdr[i] (i.e. cdr[0], cdr[1], cdr[2], etc.) get set to the same value. Subsequent loops then don't change any of them.

I suspect there's something going on with operator precedence or I'm dereferencing wrong, but I haven't been able to figure it out.

Thanks.

UPDATE

I put together the following simplified code, which seems to work fine. But while the output is completely as expected, I'm still getting the same weird behaviour as I step through it in the debugger. I'm starting to think the fundamental problem with my code might be elsewhere, and I've just been diverted by issues with the debugger (or probably just with my misunderstanding of the output). Is there a known reason why a watch on 'cdr[0]', 'cdr[1]', etc. in Visual Studio wouldn't show what I'm expecting it to show?

#include "stdafx.h"
#include "stdio.h"
#include "stdlib.h"

#define NUM_REGIONS 50


void printArray(double *inVec, int len){
    int i;
    for(i=0; i<len; i++) printf("%f\t",inVec[i]);
    printf("\n");
}

int main(array<System::String ^> ^args){

    int numRatings = 25, remQuarters = 100, i, j, k;
    double ***cdr;
    char dummy;

    cdr = (double ***) malloc(NUM_REGIONS * sizeof(double **)); 
    for(i=0; i<NUM_REGIONS; i++){ 
        cdr[i] = (double **) malloc(numRatings * sizeof(double *)); 
        for(j=0; j<numRatings; j++){ 
            cdr[i][j] = (double *) malloc(remQuarters * sizeof(double)); 
        } 
    }

    for(i=0; i<NUM_REGIONS; i++){
        for(j=0; j<numRatings; j++){
            for(k=0; k<remQuarters; k++){
                cdr[i][j][k] = 100*i + 10*j +k;
            }
        }
    }

    for(i=0; i<5; i++) printf("%f\t",cdr[1][1][i]);
    printf("\n");
    for(i=0; i<5; i++) printf("%f\t",cdr[3][1][i]);
    printf("\n");
    for(i=0; i<5; i++) printf("%f\t",cdr[1][3][i]);
    printf("\n");
    for(i=0; i<5; i++) printf("%f\t",cdr[i][i][i]);
    printf("\n");
    printArray(cdr[1][1], 5);
    printArray(cdr[3][3], 5);

    scanf("%c", &dummy);
    return 0;
}

Thanks again for all the feedback.

+3  A: 

I had a tough time figuring out exactly what problem you are seeing.

I tried the following for a multidimensional array of doubles and had no problems:

int i, j;
int rows = 5;
int columns = 10;
int z_axis = 5;
double ***cdr = malloc(rows * sizeof(double **));
for (i = 0; i < rows; i++)
{
  cdr[i] = malloc(columns * sizeof(double *));
  for (j = 0; j < columns; j++)
  {
    cdr[i][j] = malloc(z_axis * sizeof(double));
  }
}

See the c-faq for specifics on this very issue: http://c-faq.com/aryptr/dynmuldimary.html

Brandon Horsley
+4  A: 

A long time ago at college I concluded that multidimensional arrays in C should be emulated with 1D arrays. First one needs to allocate a buffer big enough to hold all of the elements. For a 2D array that would be ncolumns*nrows*sizeof(element). Then one accesses the array elements by transforming multidimensional indices to 1D index. For a 2D array, accessing A(i,j) translates to bufA[i*ncolumns+j].

ssegvic
That did occur to me and may be the way I end up going. I just thought the array notation would be easier to read and understand. But I guess that only matters if it works!
Mark
+1  A: 

The problem is with your dereferencing. cdr[i][j] won't do what you want. When you assign cdr[i], you're putting a pointer at that index. However, cdr[i][j] doesn't dereference cdr[i]. It assumes you have a rectangular block of memory and adds the appropriate value to cdr to determine where cdr[i][j] lives. You're probably going to have to do the dereferencing manually with *s.

Nathon
?!? I use this technique all the time to make ragged arrays.
Joshua
It behaves exactly as intended. In this case, `cdr[i][j] != cdr[i*rowlen+j]`. In the question, `cdr` has type `double ***`. That makes `cdr[i]` have type `double **` and `cdr[i][j]` have type `double *`. The syntax is correct.
Jeff M
A: 

What is cdr supposed to be? A 3D array of double (which is what I'm assuming, based on your snippet)? A 2D array of pointer to double? Or a pointer to a 2D array of double?

Assuming the first:

#include <stdlib.h>

int main(void)
{
  double ***cdr = malloc(sizeof *cdr * NUM_REGIONS);
  if (cdr)
  {
    size_t i;
    for (i = 0; i < NUM_REGIONS; i++)
    {
      cdr[i] = malloc(sizeof *cdr[i] * numRatings);
      if (cdr[i])
      {
        cdr[i][j] = malloc(sizeof *cdr[i][j] * remQuarters);
      }
    }
  }
  return 0;
}

A few things to note:

  1. Don't cast the return value of malloc(). As of C89, you don't need to (void pointers are implicitly converted to the target pointer type), and if you forget to include stdlib.h or otherwise don't have a prototype for malloc() in scope, the cast will suppress a useful "incompatible type for assignment" diagnostic.
  2. Use the sizeof operator on the object being allocated, not a type expression. This protects you in case the type of the target object changes; it also helps with readability, I think.

This should work; you should have a 3D array of double equivalent to

double cdr[NUM_REGIONS][numRatings][remQuarters];
John Bode
I can see a bit more context would help. I'm not clear how a 3D array of double is different from a 2D array of pointer to double. I'm trying to get characteristics of both. Ultimately, I'm trying to send `cdr[region][rating]` as a pointer to double to a function. That function takes `double *cdr1` as an argument, then loops through all *remquarters* values as `cdr1[i]`. Does that make sense? Also, thanks for the general 'good housekeeping' notes.
Mark
Yeah, that makes sense. You might save yourself some grief by simply declaring cdr as a 3D VLA (if you have a C99 compiler available; if not, you're stuck with the piecewise allocation). Remember that the type of an array expression "decays" from "M-element array of T" to "pointer to T" in most contexts. If you declared `cdr` as a 3D array, such as `double cdr[X][Y][Z];`, then the type of the expression `cdr[i][j]` is "Z-element array of double", but when you pass it to a function, it will "decay" to type "pointer to double."
John Bode
A: 

It turns out that the code as written worked (though it did have room for style improvements, as several people pointed out). I tracked the real problem in my code to some file input that was too big for the buffer (which is a lesson for me in error checking).

The debugger gave some weird output that had me focusing on this part of the code. I still don't understand why my debugger is doing this when others aren't, but I'll deal with that another day.

Mark