Reputation: 239
I'm trying to store three square, 2d arrays into a buffer so I use one contiguous block of memory. If the array size is rxr, my formula is:
buffer = (double*) malloc((r + r + r) * sizeof(double *) +
(r*r + r*r + r*r) * sizeof(double));
if(buffer == NULL) {
printf("out of memory\n");
return 0;
}
for(i = 0, j = 0; i < r; i++) {
a[i] = &buffer[j];
j+=r;
}
for(i = 0; i < r; i++) {
b[i] = &buffer[j];
j+=r;
}
for(i = 0; i < r; i++) {
c[i] = &buffer[j];
j+=r;
}
a = buffer[j];
b = buffer[j + r];
c = buffer[j + r + r];
As you can see, I'm lost. a, b, c, are declared as double pointers (meant to be pointers to arrays of arrays), so I want each to have an array of size r, with each element pointing to its own separate array of size r. Any ideas...
Upvotes: 2
Views: 2094
Reputation: 224546
First, two-dimensional arrays are usually better managed as arrays of arrays rather than as pointers to rows, unless there is particular reason to use pointers. For this, we could do:
double (*Memory)[r][r] = malloc(3 * sizeof *Memory);
// Now Memory points to three r-by-r arrays of double.
double (*a)[r] = Memory[0];
double (*b)[r] = Memory[1];
double (*c)[r] = Memory[2];
However, if you want to use pointers to rows, then you should allocate space for the pointers separately from allocating space for the elements. (If you do not, there are issues with conformance to the C standard, particularly with padding and alignment. Additionally, your code treated buffer
as an array of one type, either double
or double *
. But the address arithmetic you need sometimes requires pointers to double
[when setting element addresses] and sometimes requires pointers to double *
[when setting addresses of pointers to double].) To use pointers to rows, you can allocate with this:
double *(*PointerMemory)[r] = malloc(3 * sizeof *PointerMemory);
// PointerMemory points to three arrays of r elements of pointers to double.
double (*ElementMemory)[r][r] = malloc(3 * sizeof *ElementMemory);
Then you can set up pointers to the rows:
// Set a to point to the first array of r elements of pointers to double.
double *a[r] = PointerMemory[0];
// Initialize the elements of a to point to rows of the first r-by-r array of double.
for (i = 0; i < r; ++i)
a[i] = ElementMemory[0][i];
// Set b for the second array of pointers and the second array of double.
double *b[r] = PointerMemory[0];
for (i = 0; i < r; ++i)
b[i] = ElementMemory[1][i];
// Set c for the third arrays.
double *c[r] = PointerMemory[0];
for (i = 0; i < r; ++i)
c[i] = ElementMemory[2][i];
Upvotes: 0
Reputation: 123598
So, if I'm understanding correctly, what you want are three r
x r
arrays (a
, b
, and c
), but you want all three of them stored contiguously; essentially, the backing store will be a single 3 x r
x r
array.
If the size r
is not known at compile time and you're working in C99 or a C11 implementation that supports variable-length arrays, you could do something like the following:
size_t r = ...;
double (*a)[r] = NULL;
double (*b)[r] = NULL;
double (*c)[r] = NULL;
double (*backing_store)[r][r] = malloc(3 * sizeof *backing_store);
if (!backing_store)
{
// panic and exit
}
a = backing_store[0];
b = backing_store[1];
c = backing_store[2];
You can then use a
, b
, and c
as though they were regular r
xr
arrays of double
:
a[i][j] = ...;
printf("%f\n", b[x][y]);
etc.
When you're done, you only need to free backing_store
:
free(backing_store);
Why does this work?
The expression backing_store
has type "pointer to r
-element array of r
-element array of double
. Since the expression backing_store[i]
is equivalent to *(backing_store + i)
, the subscript operator implicitly dereferences the pointer, so the type of the expression is "r
-element array of r
-element array of double
". Each of backing_store[0]
, backing_store[1]
, and backing_store[2]
is an r
x r
array of double
.
Remember that in most contexts, an expression of type "N
-element array of T
" is implicitly converted ("decays") to an expression of type "pointer to T
", and its value is the address of the first element in the array.
Thus, the expression backing_store[0]
is converted from type "r
-element array of r
-element array of double
" to "pointer to r
-element array of double
", which just happens to be the type of a
, and the value is the address of the first subarray (which happens to be the same as backing_store
). Again, applying the subscript operator implicitly dereferences the pointer, so a[i][j]
gives the j
th element of the i
th array after a
.
If r
is known at compile time (i.e., it is a constant expression) then the procedure is the same, you just don't have to declare the variable r
:
#define R ...
double (*a)[R] = NULL;
double (*b)[R] = NULL;
double (*c)[R] = NULL;
double (*backing_store)[R][R] = malloc(3 * sizeof *backing_store);
if (!backing_store)
{
// panic and exit
}
a = backing_store[0];
b = backing_store[1];
c = backing_store[2];
If r
is not known at compile time and you don't have variable-length arrays available (using C89 or a C11 compiler that doesn't support VLAs), then it can get a little messier. Here we treat backing_store
as a 1-d array of double
and compute 1-d subscripts into each subarray:
double *a = NULL;
double *b = NULL;
double *c = NULL;
double *backing_store = malloc(3 * r * r * sizeof *backing_store);
if (!backing_store)
{
// panic
}
a = backing_store;
b = backing_store + r * r;
c = backing_store + 2 * r * r;
a[i*r+j] = ...;
printf("%f\n", b[x*r+y]);
Again, you should only need to free backing_store
when you're done:
free(backing_store);
Not as pretty as using 2-d subscripts, but it should work.
Upvotes: 2
Reputation: 60848
You're confusing malloc'ing pointers with malloc'ing doubles themselves. Just allocate the doubles like:
double* all = (double*) malloc( 3*r*r*sizeof(double));
Then where do your actual matrices go?
double *p1 = all;
double *p2 = &all[r*r];
double *p3 = &all[2*r*r];
Please check for off by one errors :) But the point is you don't need to malloc out both the double* and the double.
Upvotes: 2