Passing multidimensional arrays as function arguments in C - c

In C can I pass a multidimensional array to a function as a single argument when I don't know what the dimensions of the array are going to be?
Besides, my multidimensional array may contain types other than strings.

Pass an explicit pointer to the first element with the array dimensions as separate parameters. For example, to handle arbitrarily sized 2-d arrays of int:
void func_2d(int *p, size_t M, size_t N)
{
size_t i, j;
...
p[i*N+j] = ...;
}
which would be called as
...
int arr1[10][20];
int arr2[5][80];
...
func_2d(&arr1[0][0], 10, 20);
func_2d(&arr2[0][0], 5, 80);
Same principle applies for higher-dimension arrays:
func_3d(int *p, size_t X, size_t Y, size_t Z)
{
size_t i, j, k;
...
p[i*Y*Z+j*Z+k] = ...;
...
}
...
arr2[10][20][30];
...
func_3d(&arr[0][0][0], 10, 20, 30);

You can declare your function as:
f(int size, int data[][size]) {...}
The compiler will then do all pointer arithmetic for you.
Note that the dimensions sizes must appear before the array itself.
GNU C allows for argument declaration forwarding (in case you really need to pass dimensions after the array):
f(int size; int data[][size], int size) {...}
The first dimension, although you can pass as argument too, is useless for the C compiler (even for sizeof operator, when applied over array passed as argument will always treat is as a pointer to first element).

You can do this with any data type. Simply make it a pointer-to-pointer:
typedef struct {
int myint;
char* mystring;
} data;
data** array;
But don't forget you still have to malloc the variable, and it does get a bit complex:
//initialize
int x,y,w,h;
w = 10; //width of array
h = 20; //height of array
//malloc the 'y' dimension
array = malloc(sizeof(data*) * h);
//iterate over 'y' dimension
for(y=0;y<h;y++){
//malloc the 'x' dimension
array[y] = malloc(sizeof(data) * w);
//iterate over the 'x' dimension
for(x=0;x<w;x++){
//malloc the string in the data structure
array[y][x].mystring = malloc(50); //50 chars
//initialize
array[y][x].myint = 6;
strcpy(array[y][x].mystring, "w00t");
}
}
The code to deallocate the structure looks similar - don't forget to call free() on everything you malloced! (Also, in robust applications you should check the return of malloc().)
Now let's say you want to pass this to a function. You can still use the double pointer, because you probably want to do manipulations on the data structure, not the pointer to pointers of data structures:
int whatsMyInt(data** arrayPtr, int x, int y){
return arrayPtr[y][x].myint;
}
Call this function with:
printf("My int is %d.\n", whatsMyInt(array, 2, 4));
Output:
My int is 6.

In C can I pass a multidimensional array to a function as a single argument when I don't know what the dimensions of the array are going to be?
No
If by "single argument" you mean passing just the array without passing the array dimensions, no you can't. At least not for true multidimensional arrays.
You can put the dimension[s] into a structure along with the array and claim you're passing a "single argument", but that's really just packing multiple values into a single container and calling that container "one argument".
You can pass an array of known type and number of dimensions but unknown size by passing the dimensions themselves and the array like this:
void print2dIntArray( size_t x, size_t y, int array[ x ][ y ] )
{
for ( size_t ii = 0, ii < x; ii++ )
{
char *sep = "";
for ( size_t jj = 0; jj < y; jj++ )
{
printf( "%s%d", sep, array[ ii ][ jj ] );
sep = ", ";
}
printf( "\n" );
}
}
You would call that function like this:
int a[ 4 ][ 5 ];
int b[ 255 ][ 16 ];
...
print2dIntArray( 4, 5, a );
....
printt2dIntArray( 255, 16, b );
Similarly, a 3-dimensional array of, for example, a struct pixel:
void print3dPixelArray( size_t x, size_t y, size_t z, struct pixel pixelArray[ x ][ y ][ z ] )
{
...
}
or a 1-dimensional double array:
void print1dDoubleArray( size_t x, double doubleArray[ x ] )
{
...
}
BUT...
However, it can be possible to pass "arrays of pointers to arrays of pointers to ... an array of type X" constructs that are often mislabeled as a "multidimensional array" as a single argument as long as the base type X has an sentinel value that can be used to indicate the end of the final, lowest-level single-dimensional array of type X.
For example, the char **argv value passed to main() is a pointer to an array of pointers to char. The initial array of char * pointers ends with a NULL sentinel value, while each char array referenced by the array of char * pointers ends with a NUL character value of '\0'.
For example, if you can use NAN as a sentinel value because actual data won't ever be a NAN, you could print a double ** like this:
void printDoubles( double **notAnArray )
{
while ( *notAnArray )
{
char *sep = "";
for ( size_t ii = 0; ( *notAnArray )[ ii ] != NAN; ii++ )
{
printf( "%s%f", sep, ( *notAnArray )[ ii ] );
sep = ", ";
}
notAnArray++;
}
}

int matmax(int **p, int dim) // p- matrix , dim- dimension of the matrix
{
return p[0][0];
}
int main()
{
int *u[5]; // will be a 5x5 matrix
for(int i = 0; i < 5; i++)
u[i] = new int[5];
u[0][0] = 1; // initialize u[0][0] - not mandatory
// put data in u[][]
printf("%d", matmax(u, 0)); //call to function
getche(); // just to see the result
}

Related

Printing a multidimensional array in C

I am trying to print a 2-D array in C by using pointers but I am not getting the expected output.
Program:-
#include <stdio.h>
int main()
{
int arr[2][3] = {{1,2,3},{4,5,6}};
int* p;
for ( p = arr; p <= arr+6; p++)
{
printf("%d ", *p);
}
return 0;
}
Output:-
1 2 3 4 5 6 -1116112128 1587637938 0 0 1893963109 32521 -1453950296 32766 -1453805568 1 800797033 21984 -1453949463
Could you tell me where I am wrong as the output should only be:
1 2 3 4 5 6
Could you tell me where I am wrong
The elements of arr are not integers, but arrays of 3 integers. So arr+6 is surely a different address than what you expect, since pointer arithmetic works in multiples of the size of the type in the array.
You'll always be better off using nested loops to iterate over a multidimensional array; treating it as one single-dimensional array of int leads to exactly the kinds of confusion you see here. The code is harder to understand and verify, it won't be any slower.
First, when looping through arrays of size n wth an index i, the condition for continuation should be i < n rather than i <= n, because array indexes in C run from 0 through n-1.
However, your code has a more serious error: 1-dimensional arrays can be 'decayed' into pointers to the elements' type; however, 2-dimensional arrays decay into pointers to 1-dimensional arrays. So, in your case, the type of the pointer used in the arr + 6 expression is a pointer to an array of three integers; further, when the 6 is added, that operation is performed in terms of the size of the pointed-to object, which is sizeof(int) * 3 – so, even when changing the <= to <, you will be running far beyond the actual bounds of the array.
To make the pointer arithmetic work in the correct 'units' (i.e. sizeof(int)), cast the arr to an int* before the addition (and also change the <= to <):
#include <stdio.h>
int main()
{
int arr[2][3] = { {1,2,3},{4,5,6} };
int* p;
for (p = (int*)arr; p < (int*)arr + 6; p++) {
printf("%d ", *p);
}
return 0;
}
You are trying to access the value in the wrong way, The two-dimensional array is saved as a continuous block in the memory. So, if we increment the value of ptr by 1 we will move to the next block in the allocated memory.
int arr[2][3] = {{1,2,3},{4,5,6}};
int *ptr = arr;
int i,j;
for (i = 0; i < 6; i++) {
printf("%d ", *(ptr + i));
}
return 0;
Array designators used in expressions with rare exceptions are implicitly converted to pointers to their first elements.
The type of the array elements of this array
int arr[2][3];
is int [3]. So a pointer to the first element of the array has the type int ( * )[3].
This assignment
p = arr;
where p has the type int * is incorrect because the operands of the assignment have incompatible pointer types.
At least you need to cast the right expression to the type int * like
p = ( int * )arr;
The same casting you need to use in the condition in the for loop. That is instead of
p <= arr+6
you have to write
p < ( int * )arr+6
Below there is a demonstration program that shows how to output a two-dimensional array as a two-dimensional array using pointers.
#include <stdio.h>
int main( void )
{
int arr[2][3] = {{1,2,3},{4,5,6}};
for ( int ( *p )[3] = arr; p != arr + 2; p++ )
{
for ( int *q = *p; q != *p + 3; ++q )
{
printf( "%d ", *q );
}
putchar( '\n' );
}
return 0;
}
If you want to output the two-dimensional array as a one-dimensional array then you can write
#include <stdio.h>
int main( void )
{
int arr[2][3] = {{1,2,3},{4,5,6}};
for ( int *p = ( int * )arr; p != ( int * )arr + 6; p++ )
{
printf( "%d ", *p );
}
putchar( '\n' );
return 0;
}
In
for ( p = arr; p <= arr+6; p++)
the expression arr, as an rvalue, is a pointer to the first element of the array (which is of type int [3], so each time you increment that pointer, it moves three int positions forward ---a whole row---, and so, arr + 6 points just after the sixth row of the array (if the array should ever had six rows) You can do it (with the proper explicit pointer conversions, as you are mixing pointers to int with pointers to int [3]) with the expression arr + 2 which is the addres of the first array element after the second row (and the number of rows of the array).
You can do it also declaring
int (*aux)[2][3] = &arr; /* aux is a pointer to the whole 3x2 array,
* so aux + 1 will be the position of the second
* 2D array after this one */
and then
int *end = (int *)(aux + 1);
or simply
int *end = (int *)(&arr + 1); /* see below */
(Beware that arr and &arr are both pointers and point to the same place, but they are not the same type (arr is of type int (*)[3] and &arr is of type int(*)[2][3])
So let's rewrite your code as
for (p = (int *)arr; p < end; p++)
or
for (p = (int *)arr; p < (int *)&arr + 1; p++)
would work, which seems more natural to do the calculus in complete array units than in rows or single cells (and you can change freely the dimensions of the array)
Your code would be:
#include <stdio.h>
int main()
{
int arr[2][3] = { { 1, 2, 3 }, { 4, 5, 6 } };
int *end = (int *)(&arr + 1); /* try to avoid evaluating this expression in the loop
* despite that it can be optimized to comparing
* with a constant value */
char *sep = "";
for (int *p = (int *)arr; p < end; p++)
{
printf("%s%d", sep, *p);
sep = ", ";
}
putchar('\n');
return 0;
}
(Beware that you have to use < operator and not <= as you don't want to print the value pointed by end, because it lies one place outside of the array)
Finally a note: this will work with true arrays, but not with function parameters declared as arrays, because they decay to pointers and then &arr is not a pointer to data the size of the array, but it is the address of the parameter itself, which points to the array somewhere else.

Implementing a 3D array as a pointer to a variable length array

I just learned about variable length arrays in C, and somebody suggested to me that I can realize a 3D array-like object in C simply as a pointer to a variable length array. A simple implementation would look as follows:
#include <stdio.h>
#include <stdlib.h>
int main( int argc, char *argv[] ) {
size_t ii, jj, kk,
Nx, Ny, Nz;
Nx = 2;
Ny = 3;
Nz = 4;
// initialize 3D array as pointer to a variable length array
double (*arr3d)[Ny][Nz] = calloc(Nx, sizeof *arr3d);
for( ii=0 ; ii<Nx ; ++ii )
for( jj=0 ; jj<Ny ; ++jj )
for( kk=0 ; kk<Nz ; ++kk )
printf( "arr3d[%ld,%ld,%ld] = %f, address = %p\n",
ii, jj, kk, arr3d[ii][jj][kk], &arr3d[ii][jj][kk] );
free(arr3d);
return 0;
}
In my understanding, one advantage (besides of the one-line implementation) is that arr3d can be simply passed to a function,
int doSomething( size_t dim1, size_t dim2, size_t dim3, double arr3d[dim1][dim2][dim3] );
by calling this function in the main function as follows:
doSomething( Nx, Ny, Nz, arr3d );
My question now, is this a valid implementation of a 3D array-like object or will I run into some trouble ? Note that in the actual case, the dimensions are larger by roughly a factor of 1000 each.
If you have an array like
double a[Nx][Ny][Nz];
then you may write
double (*arr3d)[Ny][Nz] = a;
that is the pointer arr3d points to the first element of the array a that has the type double{Nt][Nz].
So this memory allocation
double (*arr3d)[Ny][Nz] = calloc(Nx, sizeof *arr3d);
is correct. There is in fact allocated dynamically a thre-dimensional array and the pointer arr3d points to the first element of the array.

C iki boyutlu dizi Malloc

I don't know where I'm wrong and I get segmentation fault error. Can you help?
My Code;
#include <stdio.h>
#include <stdlib.h>
void do_array(int x, int y, int **arr){
arr=(int **)malloc(sizeof(int)*x);
for (int i=0;i<y;i++){
*(arr+i)=(int *)malloc(sizeof(int)*y);
}
}
int main(){
int **p;
do_array(5,2,p);
for (int i=0;i<5;i++){
for (int j=0;j<2;j++){
*(*(p+i)+j)=i;
}
}
for (int i=0;i<5;i++){
for (int j=0;j<2;j++){
printf("%d\n",*(*(p+i)+j));
}
}
return 0;
}
!!! I want to do is create a dynamic 2D array
The formal argument arr in the function is a separate object from the actual argument p in main - any changes to arr are not applied to p, so p is never set to point to the memory you just allocated.
What you will have to do is pass a pointer to p:
void doArr(int x, int y, int ***arr){
*arr=(int **)malloc(sizeof(int *)*x); // note type of sizeof - you're allocating an array of int *, not int
for (int i=0;i<y;i++){
*(*arr+i)=(int *)malloc(sizeof(int)*y);
}
}
...
doArr( 5, 2, &p );
Or, return arr and assign the result of doArr to p:
int **p = doArr( int x, int y )
{
int **arr = malloc(...);
...
return arr;
}
p = doArr( 5, 2 );
As Emanuel P notes, do is a keyword, so you can't use it as a function name.
A couple of style notes:
Since C89, malloc returns void * and does not require an explicit cast, and many of us will recommend against using it. It just adds visual noise and creates an extra maintenance burden.
Similarly, I often recommend that the operand of sizeof be your target expression, not a type name. Again, this eases your maintenance headaches if you change the type of the destination pointer.
Use array notation instead of pointer arithmetic where possible. It's easier to read and follow, and you're less likely to make a mistake.
Finally, always check the result of a malloc, calloc, or realloc call.
I'd rewrite your doArr function as follows:
void doArr(int x, int y, int ***arr)
{
*arr = malloc( sizeof **arr * x );
if ( *arr )
{
for ( int i = 0; i < y; i++ )
{
(*arr)[i] = malloc( sizeof *(*arr)[i] * y );
}
}
}
and call it as
doArr( 5, 2, &p );
int main(){
int **p /* = NULL */ ; // p is garbage or NULL
do_array(5,2,p); // p is still garbage or NULL
for (int i=0;i<5;i++){
for (int j=0;j<2;j++){
*(*(p+i)+j)=i; // p is still garbage or NULL
I want to do is create a dynamic 2D array
In your code you do not create a 2D array only the array of pointers.
void *doArr(size_t x, size_t y)
{
int (*arr)[x][y] = malloc( sizeof((*arr)[0][0]) * x * y);
return arr;
}

Can I declare a pointer to a 2d array before I know the dimensions?

I know that one way to declare a pointer to a 2d array is like this:
int(*p)[100];
Then after assigning it to something, I can use elements like this:
p[1][6] = 18;
But let's say I don't yet know the dimensions of the array and I intend to malloc them when I find out.
One solution is that I declare a pointer to just an int, then use pointer arithmetic to navigate the array. I usually do this, but this time I'd like to use the square bracket notation for convenience.
So how do I declare this pointer when I don't yet know the dimensions and I intend to use square bracket notation?
So how do I declare this pointer when I don't yet know the dimensions and I intend to use square bracket notation?
You can use a pointer to a pointer.
int** p = NULL;
and later....
p = malloc(N*sizeof(int*));
for (int i = 0; i < N; ++i )
{
p[i] = malloc(M*sizeof(int));
}
and make sure to deallocate in multiple steps.
for (int i = 0; i < N; ++i )
{
free(p[i]);
}
free(p);
Another way.
// Allocate memory for the pointers.
p = malloc(N*sizeof(int*));
// Allocate memory for the ints.
p[0] = malloc(M*N*sizeof(int));
// Assign the values to the elements of p.
for (int i = 1; i < N; ++i )
{
p[i] = p[i-1] + M;
}
and deallocate in only two steps.
free(p[0]);
free(p);
Simply use a pointer to a variable length array.
Your sizes, y rows with x elements:
size_t x = 123;
size_t y = 30;
Allocate in with one call, sizeof(*p) is identical to sizeof(int) * 123:
int (*p)[x] = malloc( sizeof(*p) * y );
And iterate the 2d array:
for( size_t i = 0 ; i < y ; i++ )
for( size_t j = 0 ; j < x ; j++ )
p[i][j] = 0;
int(*p)[100];
This is not you want . This is pointer to array of int.
What you want is pointer to pointer to int.
int **p;
p=malloc(sizeof(int *)*r); // allocate memory for r number of int *
for(int i=0;i<r;i++)
p[i]=malloc(sizeof(int)*c); // allocate memory to each pointer
free in similar manner.
To index an array of arrays rather than an array of pointers, you can use this trick:
#include <stdlib.h>
#include <stdio.h>
void f( const size_t m, const size_t n, const char s[m][n] )
{
printf( "%s, %s!\n", s[0], s[1] );
return;
}
int main(void) {
static const char hello[][6] = { "hello", "world" };
f( sizeof(hello)/sizeof(hello[0]), sizeof(hello[0]), hello );
return EXIT_SUCCESS;
}
Your question is tagged C and not C++, but C++ does have references to arrays: int (&foo)[m][n] = bar;

segmentation fault when writing to a char to a char array

I am writing a code to processing a 2-d char array.
char *board[] = {"aa"};
I pass the array to a function
search(board, row, col);
..........
bool search(char** boards, int row, int col)
{
// Inside the function, I want to modify the boards[x][y]
// from the board within x and y, x < row, y < col
// I get the segmentation fault here because the boards is
// allocated to the static memory and cannot be changed.
boards[x][y] = 'c';
}
In C++, I can use
vector< vector<char> boards
to change the elements in the specific location.
But in C, there is no vector.
Is anyone has good advice about how to modify the boards 2-D array?
Thanks.
The problem is that you are making an array of pointers, and set the pointers to ppint to string constants. Memory of string constants is not modifiable, so writing to it causes segmentation fault.
Changing an array of pointers to 2D array of characters will fix this problem
char board[][10] = {"aa", "bb"};
In C you can use for example Variable Length Arrays if the compiler supports them.
For example
int size_t m = 2;
int size_t n = 3;
char board[m][n] = { "aa", "bb" };
search( m, n, board );
// Add more search code here...
bool search( size_t rows, size_t cols, char board[][cols] );
Another approach is to use an arrays of pointers where each pointer points to a dynamically allocated array or the array itself can be dynamically allocated. For example
int size_t m = 2;
int size_t n = 3;
char **board;
board = malloc( m * sizeof( char * ) );
for ( size_t i = 0; i < m; i++ ) board[i] = malloc( n * sizeof( char ) );
strcpy( board[0], "aa" );
strcpy( board[1], "bb" );
search( board, m, n );
//,,,
bool search( char **board, size_t rows, size_t cols );
In this case you have to free the allocated memory when array will not be needed any more.
As for the segmentation fault you got then you are trying to change a string literal. They are immutable. Any attempt to modify a string literal results in undefined behaviour.
Try declaring board as 2D array,
char board[10][10]
Also change the search function declaration to use board as a 2D array like:
Search(char board[][], int x, int y) {...
I think that your code is fails because you mix arrays of pointers with an argument which expect a pointer pointer to char.
Regards

Resources