Reputation: 51
I need help understanding how to use point arithmetic for 2D arrays in C. I'm using this website (http://www.geeksforgeeks.org/dynamically-allocate-2d-array-c/) as a reference (using example 1, a single pointer).
int numRows = 2;
int numColumns = 3;
double * arrayMatrix = malloc(numRows * numColumns * sizeof(double));
int row = 0;
int column = 0;
printf("\nPlease enter the elements of your augmented matrix:\n");
for(row = 0; row < numRows; row++)
{
for(column = 0; column < numColumns; column++)
{
printf("A[%d][%d]:", row + 1, column + 1);
scanf("%lf", &arrayElement);
printf("\n");
*(arrayMatrix + row * numColumns + column) = arrayElement;
//arrayMatrix[row + numColumns + column] = arrayElement;
}
}
// TEST PRINT
for(row = 0; row < numRows; row++)
{
for(column = 0; column < numColumns; column++)
{
printf("%5.2lf", *(arrayMatrix + row * numColumns + column));
//printf("%5.2lf", arrayMatrix[row + numColumns + column]);
}
printf("\n");
}
I need help understanding if this is a correct way to input data into a 2D array and if it is also a correct way to print data from a 2D array. I'm using the example data of for row 1 as {1, 2, 3} and row 2 as {1, 2, 3}; however when printing out the information all I get are 0 for all 6 elements.
I also used this answer as reference (How to use pointer expressions to access elements of a two-dimensional array in C?). Specifically following this line:
int x = *((int *)y + 2 * NUMBER_OF_COLUMNS + 2); // Right!
But I'm using a double pointer instead of an integer, but I don't know if that is causing my issues or if it is something else.
Edit - Updated the code a little but it is still not working at all.
Edit 2: This is the most recent update with the code that I've been trying to get to work. All 3 ways of inputting and printing data from the array result in the same result (0's for all values in the array).
int numRows = 2;
int numColumns = 3;
//double * arrayMatrix = malloc(numRows * numColumns * sizeof(double));
double (*arrayMatrix)[numColumns] = malloc(sizeof(double[numRows][numColumns]));
int row = 0;
int column = 0;
printf("\nPlease enter the elements of your augmented matrix:\n");
for(row = 0; row < numRows; row++)
{
for(column = 0; column < numColumns; column++)
{
printf("A[%d][%d]:", row + 1, column + 1);
scanf("%lf", &arrayElement);
printf("\n");
//*(arrayMatrix + row * numColumns + column) = arrayElement;
//arrayMatrix[row + numColumns + column] = arrayElement;
arrayMatrix[row][column] = arrayElement;
}
}
// TEST PRINT
for(row = 0; row < numRows; row++)
{
for(column = 0; column < numColumns; column++)
{
//printf("%5.2lf", *(arrayMatrix + row * numColumns + column));
//printf("%5.2lf", arrayMatrix[row + numColumns + column]);
printf("%5.2lf", arrayMatrix[row][column]);
}
printf("\n");
}
Upvotes: 0
Views: 11423
Reputation: 141618
Your first code sample looks correct, if you had double arrayElement;
earlier.
However, if perhaps you had it as int arrayElement;
or some other data type instead, it would explain what you are seeing, because %lf
can only be used with double
.
Modern compilers are able to give a warning for this problem, so you could investigate which compiler switches you are using to make sure you are getting the maximum possible warning level.
To avoid this error you could scan directly into the array:
scanf("%lf", &arrayMatrix[row * numColumns + column]);
NB. You seem to have got alternative variations of each line that accesses the array, where you change from pointer notation to index notation. Generally speaking, index notation is considered easier to read and therefore preferable.
Upvotes: 1
Reputation: 84559
I think I understand what you are wanting to do. Before we talk code, let's back up and bit and learn a little about the different ways a 2D array can be simulated in C. You have 2 basic approaches. You can statically or dynamically declared an array array[row][col] = {{r0c0, r0c1, r0c2, ...}, {r1c0, r1c1, r1c2, ...} ... };
which will create a single block of memory, with the values stored sequentially r0c0, r0c1, r0c2, ..., r1c0, r1c1, r1c2, ...
., or you can create row
number of pointers with each pointing to an individual array of col
elements. The second method (the row
number of pointers each pointing to arrays of col
elements) need not be sequential in memory. It can be, but there is no requirement that it is.
The array index notation array[i][j]
will take care of handling the offset within a sequential block of memory to provide access to any individual element. The same holds true for access to any element of within the individual col
sizes array pointed to by array[i]
. But what is actually happening?
Let's look at the notation of a simple 4-element array, say array[4]
. To access any element, you could request any element array[0]
to array[3]
to access all 4 elements. What is array[2]
really? You know array
is also a pointer. You know that to access the value at the address held by the pointer you need to dereference the pointer. To access array[0]
, you could simply write *array
, but how would you access the 2nd element with pointer notation?
As we discussed earlier, all the elements in an array declared as we have declared this example are stored sequentially in memory. So, to access any element in the array, all you need is the offset from the beginning of the array. Since you know the beginning address of the array is just array
, if you wanted the 2nd element, you would need to access the element offset 1
from the beginning, or at *(array + 1)
-- try it. In fact you can access all elements at offsets 0-3
from the beginning with *(array + i)
where i
is a number in 0-3
.
Looking back a bit, that also explains why you can access the 1st element in an array by simply using *array
. If you wrote the full syntax for the first element you would have *(array + 0)
-- and you know the + 0
is doing nothing so that is why you can access the 1st element with *array
because *(array + 0) = *array
.
OK, what about the 2D case? If array[x]
is *(array + x)
, what is array[x][y]
? Break it down. You know you can write array[x]
as *(array + x)
, so array[x][y]
can be written *(array + x)[y]
(and if we substitute stuff
for *(array + x)
for the moment, we can write stuff[y]
. We know how to write that in pointer notation: *(stuff + y)
, right? Now just substitute *(array + x)
for stuff
and you get *(*(array + x) + y)
. That is your full pointer notation for accessing any element within a sequential block of memory in a simulated 2D array manner and that is what is happening behind the scene when you write array[x][y]
.
Now let's talk pointer arithmetic. When you declare a pointer, you are declaring a pointer to a specific type
(except in the case of void
). That type
tells the compiler how to handle the arithmetic with that pointer. For example, if you declare:
char array[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
char *p = array;
The compiler knows that each char
occupies 1-byte
of memory, so when you write *(p + 1)
, you are asking for the char
value 1-byte
from the beginning of array
. The same holds true if you write p++;
then *p
. But what happens with:
int array[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
int *p = array;
By virtue of knowing the type
is int
and that an int
is 4-bytes
(platform dependent), when you write *(p + 1)
or p++; *p;
, you will get the 2nd element in the array, but the value is 4-bytes
from the beginning of the array. The type
tells the compiler how to handle pointer arithmetic (i.e. the offset) for any given value.
You can cement that for yourself with a minimal example:
#include <stdio.h>
#define ROWS 2
#define COLS 2
int main (void) {
int a[ROWS][COLS] = {{ 1, 2 }, { 3, 4 }};
int *p = *a;
int i, j;
for (i = 0; i < ROWS; i++) {
for (j = 0; j < COLS; j++)
printf (" %2d", a[i][j]);
putchar ('\n');
}
/* using a pointer to access the values */
for (i = 0; i < ROWS * COLS; p++, i++)
printf (" %2d", *p);
putchar ('\n');
return 0;
}
Output
$ ./bin/array_min
1 2
3 4
1 2 3 4
Now on to what you actually asked about. Given all we have discussed, what are you declaring when you declare:
double (*arrayMatrix)[NCOLS] = calloc (NROWS, NCOLS * sizeof **arrayMatrix);
You are declaring and allocating space for a pointer to what? A pointer to an array of doubles
with NCOLS
elements. How many of those do you need to hold the complete array? You will need NROWS
number of pointers to arrays holding NCOLS
elements each. Note above the use of calloc
instead of malloc
. There is an important, but subtle difference, in both the syntax and what they do. malloc
will allocate memory for you, but that memory is uninitialized and can contain all manner of stuff. calloc
also allocates, but then initializes the memory to zero. Which when dealing with numeric arrays is useful to prevent the accidental access of an uninitialized element (resulting in undefined behavior). There is a marginal speed difference between malloc
and calloc
as the result, but you will be hard pressed to find a measurable difference in anything less than a few million allocations.
With that background, consider again what you are trying to do. With very few tweaks, you would have it make more sense if you did something like:
#include <stdio.h>
#include <stdlib.h>
#define NROWS 2
#define NCOLS 3
int main (void) {
size_t row = 0;
size_t col = 0;
/* allocate NROWS (array of pointers to NCOLS doubles). using
* calloc will allocate and initialize all elements to zero.
*/
double (*arrayMatrix)[NCOLS] = calloc (NROWS, NCOLS * sizeof **arrayMatrix);
/* prompt user for input, validate a proper value is entered */
printf("\nPlease enter the elements of your augmented matrix:\n");
for(row = 0; row < NROWS; row++)
{
for(col = 0; col < NCOLS; col++)
{
while (printf(" A[%zu][%zu]: ", row + 1, col + 1) &&
scanf("%lf", &arrayMatrix[row][col]) != 1)
printf("\n");
}
}
/* printf the array of pointers */
printf ("\n The matrix entered was:\n\n");
for(row = 0; row < NROWS; row++)
{
for(col = 0; col < NCOLS; col++)
{
printf(" %5.2lf", arrayMatrix[row][col]);
}
printf("\n");
}
free (arrayMatrix);
return 0;
}
Output
$ ./bin/arraymatrix
Please enter the elements of your augmented matrix:
A[1][1]: 1
A[1][2]: 2
A[1][3]: 3
A[2][1]: 4
A[2][2]: 5
A[2][3]: 6
The matrix entered was:
1.00 2.00 3.00
4.00 5.00 6.00
Memory Error/Leak Check
In any code your write that dynamically allocates memory, you have 2 responsibilites regarding any block of memory allocated: (1) always preserves a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed. It is imperative that you use a memory error checking program to insure you haven't written beyond/outside your allocated block of memory and to confirm that you have freed all the memory you have allocated. For Linux valgrind
is the normal choice. There are so many subtle ways to misuse a block of memory that can cause real problems, there is no excuse not to do it. There are similar memory checkers for every platform. They are all simple to use. Just run your program through it.
$ valgrind ./bin/arraymatrix
==17256== Memcheck, a memory error detector
==17256== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==17256== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==17256== Command: ./bin/arraymatrix
==17256==
Please enter the elements of your augmented matrix:
A[1][1]: 1
A[1][2]: 2
A[1][3]: 3
A[2][1]: 4
A[2][2]: 5
A[2][3]: 6
The matrix entered was:
1.00 2.00 3.00
4.00 5.00 6.00
==17256==
==17256== HEAP SUMMARY:
==17256== in use at exit: 0 bytes in 0 blocks
==17256== total heap usage: 1 allocs, 1 frees, 48 bytes allocated
==17256==
==17256== All heap blocks were freed -- no leaks are possible
==17256==
==17256== For counts of detected and suppressed errors, rerun with: -v
==17256== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)
Let me know if you have further questions. My fingers are tired. This ended up much longer than I anticipated...
Upvotes: 7
Reputation: 229
Here is a function that will allocate a contiguous 2d array of doubles:
double* alloc_array_2d(int Width, int Height){
return (double*)malloc(Width * Height * sizeof(double));
}
Here is a function that will get a pointer to an element of the array based on an x and y coordinate:
double* get_element_2d(double* Array, int Width, int X, int Y){
int index = (Width * Y) + X;
return Array[index];
}
With this method a 5 x 3 array could be imagined to look like this:
0 1 2 3 4
5 6 7 8 9
10 11 12 13 14
In computer memory it actually looks like this:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
The basic idea is that the beginning of one row of values sits beside the end of the previous row of values, so by multiplying the width of the array by the vertical coordinate you offset the index into the correct row. For example to access the third object in the second row (7) we call
get_element_2d(myArray, 5, 2, 1); // 1 * 5 + 2 = 7
Note that each of the coordinates starts at zero just like with all pointer arithmetic, so to access an element in the first row you would use get_element_2d(Array, Width, X, 0)
Upvotes: 1