void foo(double **A, double **B) {
//....
}
int main() {
double **A;
double B[10][10];
int i, j;
A = (double **)malloc(sizeof(double*) * 10);
for (i = 0; i < 10; i++) {
*(A+i) = (double*)malloc(sizeof(double) * 10);
for (j = 0; j < 10; j++) {
*(*(A+i)+j) = i * j;
}
}
foo(A, B);
return 0;
}
This gives me a warning
warning: incompatible pointer types passing 'double [10][10]' to parameter of type 'double **'
[-Wincompatible-pointer-types]
From my understanding B holds a pointer to a pointer of type double. Am I not doing the same thing with A and B. Why am I getting this warning only for B?
B is an array of arrays of 10 double, a very different type from an array of pointers to arrays of double. Change foo's prototype to:
void foo(double **A, double (*B)[10])
Also simplify the code in main this way:
int main() {
double **A;
double B[10][10];
int i, j;
A = malloc(10 * sizeof(*A));
for (i = 0; i < 10; i++) {
A[i] = malloc(10 * sizeof(*A[i]));
for (j = 0; j < 10; j++) {
A[i][j] = i * j;
}
}
foo(A, B);
return 0;
}
The primary difference between passing a pointer to array and pointer to pointer from a syntax standpoint isn't too difficult to understand. When you pass a pointer to array, either array[x][y] or (*array)[y], you specify the parameter as either:
somefunc (type array[][y])
or
somefunc (type (*array)[y])
The rule to take away -- when passing a pointer to array, you must always pass the number of columns involved.
On the other hand, when passing a pointer-to-pointer-to-type, you only need to pass a pointer. e.g.:
somefunc (type **array)
The primary difference in "Why?" has to do with how the information is stored in memory. Take for example int array[x][y]. There you have x * y integers stored in a sequential block of memory. x and y provide a direct index to an integer within that sequential block. (anywhere within the x arrays containing y values each).
On the other hand, with int **array, you have a pointer to pointer -- meaning that your array[x] value identifies points to another pointer holding the beginning address of an array of y values. There is no requirement that any of the separate pointers identified by array[0], array[1], ... be stored in any sequential manner.
Take the following example. You have array (your typical array[x][y] or 2D array as it is often referred to) and arraydp an array of pointer-to-pointer-to-type (your typical double-pointer). The example shows the different way you must handle passing each.
The tweak in the game is that a function can only return a single value (a pointer), so to return a reference to array[x][y] it must be returned as a double-pointer and recast appropriately.
#include <stdio.h>
#include <stdlib.h>
#define MAX 5
/* function prototypes */
int **alloc_fill (size_t n);
int **alloc_fill_dp (size_t n);
void prn_array (int (*a)[MAX], size_t nrow);
void prn_array_dp (int **a, size_t nrow, size_t ncol);
int main (int argc, char **argv) {
int (*array)[MAX] = { NULL }; /* pointer to array of MAX ints */
int **arraydp = NULL; /* pointer to pointer to int */
size_t i, n;
n = argc > 1 ? atoi(argv[1]) : 5; /* set number of rows */
/* fill 'n' pointer to array[MAX] */
array = (int (*)[MAX])alloc_fill (n);
/* fill 'n' pointer to pointer to int */
arraydp = alloc_fill_dp (n);
if (!array || !arraydp ) { /* validate both allocated */
fprintf (stderr, "error: alloc_fill failed.\n");
return 1;
}
printf ("\n elements of '%zu' arrays:\n\n", n);
prn_array (array, n);
printf ("\n elements of '%zu' arrays:\n\n", n);
prn_array_dp (arraydp, n, MAX);
free (array); /* single call to free for 'array' */
for (i = 0; i < n; i++) /* free each pointer, then arraydp */
free (arraydp[i]);
free (arraydp);
return 0;
}
/* allocate/fill 'n' pointer to array of MAX int */
int **alloc_fill (size_t n)
{
int (*a)[MAX] = { NULL };
size_t i, j;
if (!(a = calloc (n, sizeof **a * MAX))) {
fprintf (stderr, "error: virtual memory exhausted.\n");
return NULL;
}
for (i = 0; i < n; i++)
for (j = 0; j < MAX; j++)
a[i][j] = (i + 1) * (j + 1);
return (int **)a;
}
/* allocate/fill 'n' pointer to pointer to type int */
int **alloc_fill_dp (size_t n)
{
int **a = NULL;
size_t i, j;
/* allocate 'n' pointers */
if (!(a = calloc (n, sizeof *a))) {
fprintf (stderr, "error: virtual memory exhausted.\n");
return NULL;
}
for (i = 0; i < n; i++) {
/* allocate MAX ints */
if (!(a[i] = calloc (MAX, sizeof **a))) {
fprintf (stderr, "error: virtual memory exhausted.\n");
return NULL;
}
for (j = 0; j < MAX; j++)
a[i][j] = (i + 1) * (j + 1);
}
return a;
}
/* print function for 'nrow' pointers
* to array of 'MAX' ints
*/
void prn_array (int (*a)[MAX], size_t nrow)
{
size_t i,j;
for (i = 0; i < nrow; i++) {
for (j = 0; j < MAX; j++)
printf (" %4d", a[i][j]);
// putchar ('\n');
putchar ('\n'), putchar ('\n');
}
}
/* printf function for 'nrow' pointers
* to pointer to 'ncol' ints
*/
void prn_array_dp (int **a, size_t nrow, size_t ncol)
{
size_t i,j;
for (i = 0; i < nrow; i++) {
for (j = 0; j < ncol; j++)
printf (" %4d", a[i][j]);
// putchar ('\n');
putchar ('\n'), putchar ('\n');
}
}
Output
$ ./bin/array_ptr_to_array
elements of '5' arrays:
1 2 3 4 5
2 4 6 8 10
3 6 9 12 15
4 8 12 16 20
5 10 15 20 25
elements of '5' arrays:
1 2 3 4 5
2 4 6 8 10
3 6 9 12 15
4 8 12 16 20
5 10 15 20 25
Difference of Storage in Memory
Here in memory is where the rubber meets the road. I you look below, you have the debugger (gdb) depiction of the memory layout for both array and arraydp. Notice with array all values are sequential. However, with arraydp, the first 5 values are the pointer address that point to each of the respective 5 int arrays that make up the values for arraydp. If you then examine pointer address for arraydp[0-4], you then may index each of the individual values:
array in memory:
(gdb) x/25d array
0x603010: 1 2 3 4
0x603020: 5 2 4 6
0x603030: 8 10 3 6
0x603040: 9 12 15 4
0x603050: 8 12 16 20
0x603060: 5 10 15 20
0x603070: 25
arraydp in memory:
(gdb) x/49d arraydp
0x603080: 6303920 0 6303952 0
0x603090: 6303984 0 6304016 0
0x6030a0: 6304048 0 33 0
0x6030b0: 1 2 3 4
0x6030c0: 5 0 33 0
0x6030d0: 2 4 6 8
0x6030e0: 10 0 33 0
0x6030f0: 3 6 9 12
0x603100: 15 0 33 0
0x603110: 4 8 12 16
0x603120: 20 0 33 0
0x603130: 5 10 15 20
0x603140: 25
(gdb) x/5d 6303920
0x6030b0: 1 2 3 4
0x6030c0: 5
(gdb) x/5d 6303952
0x6030d0: 2 4 6 8
0x6030e0: 10
(gdb) x/5d 6303984
0x6030f0: 3 6 9 12
0x603100: 15
(gdb) x/5d 6304016
0x603110: 4 8 12 16
0x603120: 20
(gdb) x/5d 6304048
0x603130: 5 10 15 20
0x603140: 25
From a programming standpoint, the differences may seem subtle, but they are critical from a syntax standpoint. Look it over and let me know if you have further questions.
Try void foo(double **A, double B[10][10]) then pass it foo(A, B)
Related
I am new to C programming and especially to pointers. In the program I wrote, I tried to write a function that returns a pointer to specified column of array. See the code below for better understanding (or confusion :) ):
#include <stdio.h>
#include <stdlib.h>
// function for getting pointer to specidifed column index
// 'ind' is index of requested column, 'ncol' is number of items in column
int* get_col(const int* arr, unsigned int ind, unsigned int ncol);
int main() {
unsigned int n;
printf("Input matrix size : ");
scanf("%i", &n);
int arr[n][n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++)
arr[i][j] = i * n + j;
}
for (int i = 0; i < n; i++) {
printf("values in column %d: \n", i);
int *col = get_col((int*)arr, i, n);
for (int j = 0; j < n; j++) {
printf("%d ", *col);
col = col + 1;
}
printf("\n");
}
return 0;
}
int* get_col(const int* arr, unsigned int ind, unsigned int ncol) {
int *result = malloc(sizeof(int) * ncol);
for (int i = 0; i < ncol; i++)
*result = *(arr + i*ncol + ind);
return result;
}
As you see get_col function accepts pointer to array, column index and column size (n of elements in column, i.e number of rows) as arguments and trying to return a pointer to 1D array that contains values of column at requested index. The problem is that result is not correct. In case n=3 results are like below:
Input matrix size : 3
values in column 0:
6 0 0 // supposed to be 0 3 6
values in column 1:
7 0 0 // supposed to be 1 4 7
values in column 2:
8 0 0 // supposed to be 2 5 8
I think that the problem lies in my understanding of pointers not the algorithm implemented. Actually, at first I didn't use pointer in my get_col function like below:
int result[ncol];
// ... do my work here to populate array
return &result;
Then as compiler complains warning: function returns address of local variable [-Wreturn-local-addr], I converted result from array to pointer in get_col function like above. What is the problem in this code? Did I use pointers in get_col function as it should be?
In the following line:
*result = *(arr + i*ncol + ind);
You're always writing to the same memory address.
Change it to one of the two following options:
*(result + i) = *(arr + i*ncol + ind);
result[i] = *(arr + i*ncol + ind);
Regarding your second problem when you used:
int result[ncol];
// ... do my work here to populate array
return &result;
You should understand that result variable in this case (static-memory allocation) is stored in the stack. So, after your function returns, the variable values doesn't exist anymore in the memory. That's why you need dynamic-memory allocation. In dynamic-memory allocation, that values stay in the memory until you call free by yourself.
Let's say my array looks like this. The number of rows will always match the number of columns in this program.
[0] [1] [2]
[3] [4] [5]
[6] [7] [8]
I would need to turn this into the reversed form:
[2] [1] [0]
[5] [4] [3]
[8] [7] [6]
I know that multidimensional arrays are just arrays of arrays, which hopefully shortens the scale of my question to just reversing a 1D array, but I don't know how to apply that same idea to a 2D array. The size will not always be a 3 by 3 array, but again the rows and columns will always be the same number.
Try following code, here n is the number of rows and m is the number of columns. Hopefully this will solve your problem. Happy coding!
for(i = 0; i < n; i++) {
for(j = 0; j < m/2; j++) {
int temp = arr[i][j];
arr[i][j] = arr[i][m-j-1];
arr[i][m-j-1] = temp;
}
}
If you are looking for a function to reverse 2D array, then you can use a function declaration like this: void reverse_2d_arr(int , int (*)[]);
where,
void reverse_2d_arr(int size, int arr[size][size]) {
int i = 0, j, k, temp;
while(i < size) {
j = 0;
k = size - 1;
while(j < k) {
temp = arr[i][j];
arr[i][j] = arr[i][k];
arr[i][k] = temp;
k--;
j++;
}
i++;
}
}
and call it using, reverse_2d_arr(3, arr); where arr is your 2d array and 3 its size.
Using Standard Arrays
From an efficiency standpoint, swap two-elements per-iteration when iterating over the column values. Since your array has a fixed width, start with the beginning and end elements, swap them, and continue working from the end to the middle, e.g.
void rev2d (int (*a)[COLS], int rows, int cols)
{
for (int i = 0; i < rows; i++)
for (int j = 0, k = cols - 1; j < k; j++, k--) {
int tmp = a[i][j];
a[i][j] = a[i][k];
a[i][k] = tmp;
}
}
(above the ..[j] and ..[k] elemnts are each swapped per-iteration of the inner-loop)
Or if you wanted to do the same thing using while loops and pointers to the beginning and end elements in each row (aside from iterating over the rows in reverse), you could do the following:
void rev2dptrs (int (*a)[COLS], int rows, int cols)
{
while (rows--) {
int *beg = *(a + rows), *end = *(a + rows) + cols - 1;
while (end > beg) {
int tmp = *beg;
*beg++ = *end;
*end-- = tmp;
}
}
}
In each case, for example if you had:
#define ROWS 3
#define COLS ROWS
...
int a[][COLS] = {{ 0, 1, 2 }, { 3, 4, 5 }, { 6, 7, 8 }};
Your function call would be:
rev2d (a, ROWS, COLS);
or in the second case:
rev2dptrs (a, ROWS, COLS);
Just a twist on a normal reversal.
Using Variable Length Arrays
The original intent was to avoid examples with VLA due to C11 Standard - 6.7.6.2 Array declarators(p4) "Variable length arrays are a conditional feature that implementations need not support;" and C11 Standard - 6.10.8.3 Conditional feature macros __STDC_NO_VLA__
However as pointed out in the comments, and with the real-world practicality that virtually all major compilers will continue to provide VLA, you can provide a bit more flexibility by reversing the declarations and specifying the rows and cols sizes fist and then passing the array as a VLA. The benefit is that it frees you from a constant size. For example, but functions can be re-written passing the array as a VLA:
void rev2dvla (int rows, int cols, int a[rows][cols])
{
for (int i = 0; i < rows; i++)
for (int j = 0, k = cols - 1; j < k; j++, k--) {
int tmp = a[i][j];
a[i][j] = a[i][k];
a[i][k] = tmp;
}
}
and with pointers:
void rev2dptrsvla (int rows, int cols, int a[rows][cols])
{
while (rows--) {
int *beg = *(a + rows),
*end = *(a + rows) + cols - 1;
while (end > beg) {
int tmp = *beg;
*beg++ = *end;
*end-- = tmp;
}
}
}
Here, the benefit is you are freed from the integer constant constraint on the number of elements per-row. By specifying the rows and cols parameters before the array parameter, the rows and cols values are known before the array int a[rows][cols] is specified as a parameter allowing the VLA to be a complete type.
The function calls would then be:
rev2dvla (rows, COLS, a);
and
rev2dptrsvla (rows, COLS, a);
If you understand each of the ways and how they differ from the others -- then you have sorting a 2D array under control. Let me know if you have further questions.
Putting the full example together to exercise each function above at least once and adding a print2D function, you could do something like the following:
#include <stdio.h>
#define COLS 3
void rev2d (int (*a)[COLS], int rows, int cols)
{
for (int i = 0; i < rows; i++)
for (int j = 0, k = cols - 1; j < k; j++, k--) {
int tmp = a[i][j];
a[i][j] = a[i][k];
a[i][k] = tmp;
}
}
void rev2dptrs (int (*a)[COLS], int rows, int cols)
{
while (rows--) {
int *beg = *(a + rows),
*end = *(a + rows) + cols - 1;
while (end > beg) {
int tmp = *beg;
*beg++ = *end;
*end-- = tmp;
}
}
}
void rev2dvla (int rows, int cols, int a[rows][cols])
{
for (int i = 0; i < rows; i++)
for (int j = 0, k = cols - 1; j < k; j++, k--) {
int tmp = a[i][j];
a[i][j] = a[i][k];
a[i][k] = tmp;
}
}
void rev2dptrsvla (int rows, int cols, int a[rows][cols])
{
while (rows--) {
int *beg = *(a + rows),
*end = *(a + rows) + cols - 1;
while (end > beg) {
int tmp = *beg;
*beg++ = *end;
*end-- = tmp;
}
}
}
void prn2d (int (*a)[COLS], int rows, int cols)
{
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++)
printf (" %2d", a[i][j]);
putchar ('\n');
}
}
int main (void) {
int a[][COLS] = {{ 0, 1, 2 }, { 3, 4, 5 }, { 6, 7, 8 }},
rows = sizeof *a / sizeof **a;
puts ("original:");
prn2d (a, rows, COLS); /* print original */
rev2d (a, rows, COLS); /* reverse col values using indexes */
puts ("\nreversed using indexes:");
prn2d (a, rows, COLS); /* print reversed array */
rev2dptrs (a, rows, COLS); /* reverse reversed array to restore original */
puts ("\nrestore original using pointers:");
prn2d (a, rows, COLS); /* print original */
rev2dptrs (a, rows, COLS); /* reverse col values using pointers */
puts ("\nreverse again using pointers:");
prn2d (a, rows, COLS); /* print reversed array */
rev2dvla (rows, COLS, a); /* reverse col values restoring original */
puts ("\nrestore original using VLA w/indexes:");
prn2d (a, rows, COLS); /* print original */
rev2dvla (rows, COLS, a); /* reverse col values using indexes */
puts ("\nreversed with VLA using indexes:");
prn2d (a, rows, COLS); /* print reversed array */
rev2dptrsvla (rows, COLS, a); /* reverse reversed array to restore original */
puts ("\nrestore original using VLA w/pointers:");
prn2d (a, rows, COLS); /* print original */
rev2dptrsvla (rows, COLS, a); /* reverse col values using pointers */
puts ("\nreverse again using VLA w/pointers:");
prn2d (a, rows, COLS); /* print reversed array */
}
Example Use/Output
$ ./bin/revarr2d
original:
0 1 2
3 4 5
6 7 8
reversed using indexes:
2 1 0
5 4 3
8 7 6
restore original using pointers:
0 1 2
3 4 5
6 7 8
reverse again using pointers:
2 1 0
5 4 3
8 7 6
restore original using VLA w/indexes:
0 1 2
3 4 5
6 7 8
reversed with VLA using indexes:
2 1 0
5 4 3
8 7 6
restore original using VLA w/pointers:
0 1 2
3 4 5
6 7 8
reverse again using VLA w/pointers:
2 1 0
5 4 3
8 7 6
I want declare a 2D array of doubles (double** data). I want to pass this by address to a helper function; so I pass &data and the helper function has argument double*** d.
Passed this way, the indexing that I used for the 2D array in the main function is no longer working.
Test code:
#include <stdio.h>
#include <stdlib.h>
void helperfunction(double*** data, int n, int c) {
printf("\nIn helper function\n");
for (int i = 0; i < n; i++) {
for (int j = 0; j < c; j++) {
printf("(%i %i %lf) ", i, j, *data[i][j]);
}
printf("\n");
}
}
int main(int argc, char **argv) {
int n = 4; //number of rows
int c = 4; //number of columns
double count = 0.0;
double** data = malloc(n * sizeof(double*));
for (int i = 0; i < n; i++) {
double* row = malloc(c * sizeof(double));
for (int j = 0; j < c; j++) {
row[j] = count;
count += 1.2;
}
data[i] = row;
}
printf("In main function\n");
for (int i = 0; i < n; i++) {
for (int j = 0; j < c; j++) {
printf("(%i %i %lf) ", i, j, data[i][j]);
}
printf("\n");
}
helperfunction(&data, n, c);
return 0;
}
Output:
In main function
(0 0 0.000000) (0 1 1.200000) (0 2 2.400000) (0 3 3.600000)
(1 0 4.800000) (1 1 6.000000) (1 2 7.200000) (1 3 8.400000)
(2 0 9.600000) (2 1 10.800000) (2 2 12.000000) (2 3 13.200000)
(3 0 14.400000) (3 1 15.600000) (3 2 16.800000) (3 3 18.000000)
In helper function
(0 0 0.000000) (0 1 4.800000) (0 2 9.600000) (0 3 14.400000)
Segmentation fault (core dumped)
Obviously, something is wrong with the indexing when I deference the address (*data[i][j]) in the helper function. What is causing this?
*data[i][j] does not do what you think. It is equivalent to *(data[i][j]). Your options are:
Use (*data)[i][j] instead, or
Pass data (instead of &data) and use data[i][j] since passing a triple pointer isn't necessary here.
Sorry, just figured this out. The dereferencing using [] occurs before the deferencing using *. Therefore a call to *data[i][j] will cause the segmentation fault when i != 0, since the highest level pointer is only pointing to one thing, while the two lover levels of pointers point to arrays.
A solution is to use data[0][i][j] instead.
I am trying to create 3 matrices which are dynamically typed (int, float, double) for the purpose of matrix multiplication. I've created a void** "typed" container for each matrix in the main, then passing them (as void***) into an init function to be malloc'd based on user selections for the type. The code below compiles, but I am getting a segmentation fault after one iteration of the j loop, and I can't figure out for the life of me why this happens. Similarly if I were to do the initialization in a separate 2-deep loop (take out j-loop from malloc loop), then the segmentation error occurs after one iteration of the i-loop still.
Is this even a good way to accomplish my goal of dynamic-typed matrix multiplication? Thank you very much for the help.
void initMat(int type, int matSize, void ***matA, void ***matB, void ***matC)
{
int i, j, k;
switch(type) {
case 0 :
*matA = malloc(matSize * sizeof(int*));
*matB = malloc(matSize * sizeof(int*));
*matC = malloc(matSize * sizeof(int*));
for (i = 0; i < matSize; i++) {
*matA[i] = malloc(matSize * sizeof(int));
*matB[i] = malloc(matSize * sizeof(int));
*matC[i] = malloc(matSize * sizeof(int));
for (j = 0; j < matSize; j++) {
*(int*)matA[i][j] = rand()/RAND_MAX * 10;
*(int*)matB[i][j] = rand()/RAND_MAX * 10;
*(int*)matC[i][j] = 0;
}
}
break;
case 1 :
// with float, double, etc.
break;
default :
printf("Invalid case.\n" );
}
}
int main()
{
int type = 0;
int size = 0;
void **matA, **matB, **matC;
int sizes[6] = {3, 4, 5};
int matSize = sizes[size];
printf("The selected matrix size is: %d. \n", matSize); //allows user to select matrix size
initMat(type, matSize, &matA, &matB, &matC);
// displayMat(matSize, matA);
}
To work with dynamically allocated 2d arrays, correct pointer types should be used. (int **) is a pointer to a pointer, pointing to the first element of an array of pointers which themselves point to disparate allocations. The result of this sort of code is a jagged array, but not a 2d array. The allocated memory is not guaranteed to be contiguous (as array allocations must be):
size_t num_rows = 3;
size_t num_cols = 5;
int **jagged_arr = malloc(sizeof *jagged_arr * num_rows);
for (size_t i = 0; i < num_rows; i++) {
jagged_arr[i] = malloc(sizeof *jagged_arr[i] * num_cols);
}
One possibility is to simply allocate storage for a 1d array, and calculate offsets into this array from 2d array indices. This works fine, but the result is not a 2d array:
size_t num_elems = num_rows * num_cols;
int *simulated_2d_arr = malloc(sizeof *simulated_2d_arr * num_elems);
This can't be indexed as a 2d array, but the 1d index can be calculated from the number of columns and the 2d array indices:
for (size_t i = 0; i < num_rows; i++) {
for (size_t j = 0; j < num_cols; j++) {
simulated_2d_arr[i * num_cols + j] = i * num_cols + j;
}
}
Both of these approaches have their uses, but they suffer from a disadvantage in that the resulting arrays can't be passed to functions which are meant to work with 2d arrays. That is, consider a function to print a 2d array, such as:
void print_2d_arr(size_t rows, size_t cols, int arr[][cols])
{
for (size_t i = 0; i < rows; i++) {
for (size_t j = 0; j < cols; j++) {
printf("%5d", arr[i][j]);
}
putchar('\n');
}
}
This function will work for something like:
int real_2d_arr[2][3] = { { 1, 2, 3 },
{ 4, 5, 6 } };
But it will not work for the earlier jagged_arr:
expected ‘int (*)[(sizetype)(cols)]’ but argument is of type ‘int **’
or for simulated_2d_arr:
expected ‘int (*)[(sizetype)(cols)]’ but argument is of type ‘int *’
The correct type to use when dynamically allocating 2d arrays is seen in the above error messages. For a 2d array of ints, that would be int (*)[]. This is the type that a 2d array decays to in most expressions, including function calls. So, to dynamically allocate a 2d array of ints, this would work:
size_t num_rows = 3;
size_t num_cols = 5;
int (*array_2d)[num_cols] = malloc(sizeof *array_2d * num_rows);
This allocates space for num_rows arrays of num_cols ints. Note that this does not create a VLA, but the VLA type is used. Of course, VLAs were introduced back in C99, but were made optional in C11 (though still widely supported).
As for the dynamic type part of your question, one option would be to create an enum to hold type identifiers, and pass one of these enumeration constants to whatever functions need them. These functions will need to accept (void *) arguments, which will be appropriately converted based on the type enumeration constant. This is a little more involved, but here is an example program. Note that the print_array() function works for both the dynamically allocated arrays, and for a statically sized array. Also note that there is no need for triple, or even double indirection!
#include <stdio.h>
#include <stdlib.h>
enum Type { CHAR,
INT,
FLOAT,
DOUBLE };
void * get_array(enum Type type, size_t rows, size_t cols);
void init_array(enum Type type, size_t rows, size_t cols, void *arr);
void print_array(enum Type type, size_t rows, size_t cols, void *arr);
int main(void)
{
char (*arr_char)[5] = get_array(CHAR, 4, 5);
int (*arr_int)[5] = get_array(INT, 4, 5);
double (*arr_double)[5] = get_array(DOUBLE, 4, 5);
int arr_static[][3] = { { 1, 2, 3 },
{ 4, 5, 6 },
{ 7, 8, 9 } };
if (arr_char) { // check for null pointer
init_array(CHAR, 4, 5, arr_char);
puts("4x5 array of char");
print_array(CHAR, 4, 5, arr_char);
putchar('\n');
}
if (arr_int) { // check for null pointer
init_array(INT, 4, 5, arr_int);
puts("4x5 array of int");
print_array(INT, 4, 5, arr_int);
putchar('\n');
}
if (arr_double) { // check for null pointer
init_array(DOUBLE, 4, 5, arr_double);
puts("4x5 array of double");
print_array(DOUBLE, 4, 5, arr_double);
putchar('\n');
}
puts("Statically sized 3x3 array of int");
print_array(INT, 3, 3, arr_static);
putchar('\n');
/* Cleanup */
free(arr_char);
free(arr_int);
free(arr_double);
return 0;
}
/* Returns null pointer on allocation failure */
void *get_array(enum Type type, size_t rows, size_t cols)
{
size_t array_sz = 0;
void *ret = NULL;
switch (type) {
case CHAR:
array_sz = sizeof (char) * rows * cols;
break;
case INT:
array_sz = sizeof (int) * rows * cols;
break;
case FLOAT:
array_sz = sizeof (float) * rows * cols;
break;
case DOUBLE:
array_sz = sizeof (double) * rows * cols;
break;
default:
fprintf(stderr, "Unrecognized type in get_array()");
}
if (array_sz) {
ret = malloc(array_sz);
}
return ret;
}
void init_array(enum Type type, size_t rows, size_t cols, void *arr)
{
for (size_t i = 0; i < rows; i++) {
for (size_t j = 0; j < cols; j++) {
int offset = i * cols + j;
switch (type) {
case CHAR:
{
char (*array_char)[cols] = arr;
array_char[i][j] = 'a' + offset;
break;
}
case INT:
{
int (*array_int)[cols] = arr;
array_int[i][j] = 0 + offset;
break;
}
case FLOAT:
{
float (*array_float)[cols] = arr;
array_float[i][j] = 0.0 + offset;
break;
}
case DOUBLE:
{
double (*array_double)[cols] = arr;
array_double[i][j] = 0.0 + offset;
break;
}
default:
fprintf(stderr, "Unrecognized type in get_array()");
}
}
}
}
void print_array(enum Type type, size_t rows, size_t cols, void *arr)
{
for (size_t i = 0; i < rows; i++) {
for (size_t j = 0; j < cols; j++) {
switch (type) {
case CHAR:
{
char (*array_char)[cols] = arr;
printf("%3c", array_char[i][j]);
break;
}
case INT:
{
int (*array_int)[cols] = arr;
printf("%5d", array_int[i][j]);
break;
}
case FLOAT:
{
float (*array_float)[cols] = arr;
printf("%8.2f", array_float[i][j]);
break;
}
case DOUBLE:
{
double (*array_double)[cols] = arr;
printf("%8.2f", array_double[i][j]);
break;
}
default:
fprintf(stderr, "Unrecognized type in get_array()");
}
}
putchar('\n');
}
}
Program output:
4x5 array of char
a b c d e
f g h i j
k l m n o
p q r s t
4x5 array of int
0 1 2 3 4
5 6 7 8 9
10 11 12 13 14
15 16 17 18 19
4x5 array of double
0.00 1.00 2.00 3.00 4.00
5.00 6.00 7.00 8.00 9.00
10.00 11.00 12.00 13.00 14.00
15.00 16.00 17.00 18.00 19.00
Statically sized 3x3 array of int
1 2 3
4 5 6
7 8 9
Your basic problem is that postfix operators are higher precedence than prefix operators. So when you say
*matA[i] = malloc(matSize * sizeof(int));
you are getting
*(matA[i]) = malloc(matSize * sizeof(int));
when what you want is
(*matA)[i] = malloc(matSize * sizeof(int));
so you need the explicit parenthesis to make it work. Similarly, instead of
*(int*)matA[i][j] = rand()/RAND_MAX * 10;
you need
((int**)*matA)[i][j] = rand()/RAND_MAX * 10;
I've actually been working on something exactly like this on my own. My advice on how to do this is to actually abandon the way you're doing it. From the looks of it you have an array of pointers (lets say an array of int pointers), and each one of those pointers has an array of its own, basically a variable declared as such int **exampleMatrix. There's actually a huge issue with you doing it this way which is cache misses. A better way for you to do this is as follows
#include <stdlib.h>
void *initMat(size_t rows, size_t cols, int type);
int main(void){
int *matrix = initMat();
free(matrix);
return 0;
}
void *initMat(size_t rows, size_t cols, int type){
//check for what type to allocate here
void *matrix = malloc(sizeof(int)*rows*cols);
//check for errors here
return matrix;
}
you of course will have to decide if your matrix is row or column major. I hope this makes sense, English isn't my first language and I'm not the best at explaining sometimes. If you need me to explain it better just tell me :)
The way you're allocating memory is weird.
*matA = malloc(matSize * sizeof(int*));
Where does matA point??, unless you allocated some memory and made matA point to it outside this function then you are thrashing you memory.
Malloc returns a pointer to allocated memory, it doesn't allocate it wherever your random pointer is pointing.
You also don't need three levels of indirection***matA, two is enough.
This is your code with the changes I suggested - disclaimer:I didn't actually try this but I think it'll work-:
void initMat(int type, int matSize, void ***matA, void ***matB, void ***matC)
{
int i, j, k;
switch(type) {
case 0 :
*matA = malloc(matSize * sizeof(int*));
*matB = malloc(matSize * sizeof(int*));
*matC = malloc(matSize * sizeof(int*));
for (i = 0; i < matSize; i++) {
(*matA)[i] = malloc(matSize * sizeof(int));
(*matB)[i] = malloc(matSize * sizeof(int));
(*matC)[i] = malloc(matSize * sizeof(int));
for (j = 0; j < matSize; j++) {
(*matA)[i][j] = rand()/RAND_MAX * 10;
(*matB)[i][j] = rand()/RAND_MAX * 10;
(*matC)[i][j] = 0;
}
}
break;
case 1 :
// with float, double, etc.
break;
default :
printf("Invalid case.\n" );
}
}
and when calling:
int type,matSize;
//whatever type you like
Type **matA,**matB,**matC;
//your code here
initMat(type,matSize,&matA,&matB,&matC);
//the rest of your code here
Suppose you have a function in C that accepts the dimensions for a 2d array (for simplicity's sake, say for a square nxn array), dynamically allocates the array, then returns it.
I'm aware allocating memory here might be considered somewhat bad practice to begin with, since it will need to be freed elsewhere, but suppose that's not a huge issue. I'm wondering if there's any advantages/disadvantages associated with these two variations of said function:
Variation 1 - Locally define int** variable in function, allocate/return array:
int **create_array(int n) {
// define array pointer, allocate array...
int **a_ = (int**)calloc(n,sizeof(int*));
for (int i = 0; i < n; i++)
a_[i] = (int*)calloc(n,sizeof(int));
return a_;
}
int main() {
int n = 3;
int **array2d = create_array(n)
printf("First element: %d%c",array2d[0][0],'\n');
// do stuff... etc...
}
Variation 2 - Add in-out int** parameter to function, allocate/return array:
int **create_array_2(int **a_, int n) {
// allocate array...
a_ = (int**)calloc(n,sizeof(int*));
for (int i = 0; i < n; i++)
a_[i] = (int*)calloc(n,sizeof(int));
return a_;
}
int main() {
int n = 3;
int **array2d;
array2d = create_array_2(array2d,n);
printf("First element: %d%c",array2d[0][0],'\n');
// do other stuff... etc...
}
Obviously they return the same result and achieve the same task, but is one considered to be safer/more efficient/better practice than the other? In my opinion the 2nd variation just makes things look a bit redundant, but I'm curious if there's any real differences between the two and what happens on the stack/heap when they're called. Hopefully this isn't a dumb question; it's just something I've been curious about. If anyone has insight to share, I'd appreciate it.
I'll probably try to avoid calling malloc and free to many times so this kind of approach is what I'll do:
Example 1:
#include <stdio.h>
#include <stdlib.h>
int *foo(size_t row, size_t col);
int main(void){
int *arr;
unsigned int row, col, k;
printf("Give the ROW: ");
if ( scanf("%u",&row) != 1){
printf("Error, scanf ROW\n");
exit(1);
}
printf("Give the COL: ");
if ( scanf("%u",&col) != 1){
printf("Error, scanf COL\n");
exit(2);
}
arr = foo(row, col);
for (k = 0 ; k < (row * col) ; k++){
printf("%d ",arr[k]);
}
free(arr);
}
int *foo(size_t row, size_t col){
unsigned int i, j;
int *arr = malloc(sizeof *arr * row * col);
int l = 0;
if(arr == NULL){
printf("Error, malloc\n");
exit(3);
}
for ( i = 0; i < row ; i++){
for ( j = 0 ; j < col ; j++){
arr[i * col + j] = l;
l++;
}
}
return arr;
}
Output:
Give the ROW: 6
Give the COL: 3
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
Example 2 (if you are working with the standard C):
#include <stdio.h>
#include <stdlib.h>
int (*foo(size_t row, size_t col))[];
int main(void){
size_t row, col;
printf("Give the ROW: ");
if ( scanf("%zu",&row) != 1){
printf("Error, scanf ROW\n");
exit(1);
}
printf("Give the COL: ");
if ( scanf("%zu",&col) != 1){
printf("Error, scanf COL\n");
exit(2);
}
int (*arr)[col] = foo(row, col);
for ( size_t i = 0; i < row; i++){
for( size_t j = 0; j < col; j++){
printf("%d ",*(*(arr+i)+j));
}
}
free(arr);
}
int (*foo(size_t row, size_t col))[]{
int (*arr)[col] = malloc(row * col * sizeof(int));
int l=0;
if (arr == NULL){
printf("Error, malloc\n");
exit(3);
}
for ( size_t i = 0; i < row; i++){
for( size_t j = 0; j < col; j++){
*(*(arr+i)+j) = l;
l++;
}
}
return arr;
}
Output:
Give the ROW: 6
Give the COL: 3
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
The whole point here is that the call of malloc and free in both examples takes place only one time
IMO, of the two you are better off simply returning the value. This way there's a pure and solid wall between you and the caller.
"Give me some stuff!"
"Okay, here's some stuff."
On the other hand, for actually allocating an array of fixed size, why bother with pointers? Why not declare your return type so as to be castable to a sized array?
int (*p2a)[15] = (int(*)[15])create_array_2(15, 15);
Then you would calloc(15*15,sizeof(int)) and be done.