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

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.

Related

How can i use an variable-length array globally?

I want my array to be global but i get my variables with scanf().
#include <stdio.h>
int m, n;
int main (void){
scanf("%d", &m);
scanf("%d", &n);
int array[m][n];
make_arr(array); //initializes array with random numbers
}
So what is the solution?
Edit: i use the solution of Farbod Shahinfar but i can't use it with my function:
void make_arr (int array[m][n] ){
int i , j ;
srand(time(NULL));
for(i=0;i<m;i++)
for(j=0;j<n;j++)
array[i][j] = rand() % 2;
What should i do?
You can declare a global pointer to an array and set it when you have scanned the input.
#include <stdio.h>
#include <stdlib.h>
int **array;
int m,n;
int main(void)
{
scanf("%d", &m);
scanf("%d", &n);
array = malloc(sizeof(int *) * m);
for (int i = 0; i < m; i++)
array[i] = malloc(sizeof(int) * n);
make_array(array);
// your other codes
// ....
for (int i = 0; i < m; i++)
free(array[i]);
free(array);
}
Memory allocated using malloc must be freed otherwise your program will have memory leakage problem.
Answer to the edit section:
You have declared a parameter for your make_arr function with the same name of the global variable this parameter will hide the global variable.
For clarification, if you declare a global variable you do not need to pass it as argument to a function.
void make_arr ()
{
int i , j ;
srand(time(NULL));
for(i=0;i<m;i++)
for(j=0;j<n;j++)
array[i][j] = rand() % 2;
// ....
}
Another approach is to pass the array to your function. This way you do not need the global variable at all. A possible function signature can be as below
void make_arr (int **array)
{
int i , j ;
srand(time(NULL));
for(i=0;i<m;i++)
for(j=0;j<n;j++)
array[i][j] = rand() % 2;
// ....
}
As Andrew Henle pointed out there is an awesome post on StackOverflow showing the right way of allocating multi-dimensional arrays. I have to confess that I did not knew about it.
Correctly allocating multi-dimensional arrays
Thanks to Andrew Henle for his constructive and useful comment
A note about malloc (Do read man page)
Malloc allocates memory from heap. You should check the man page of malloc for more information.
The malloc() function allocates size bytes and returns a pointer to the allocated memory. The memory is not initialized. If size is 0, then malloc() returns either NULL, or a unique pointer value that can later be successfully passed to free().
The free() function frees the memory space pointed to by ptr, which must have been returned by a previous call to malloc(), calloc() or realloc(). Otherwise, or if free(ptr) has already been called before, undefined behavior occurs. If ptr is NULL, no operation is performed.
First, given the variable-length array
int array[m][n];
your void make_arr( int array[m][n] ) won't work with int **array because int **array is not an actual 2-dimensional array. int **array is a pointer to an array of pointers to multiple and completely separate one-dimensional arrays. Such a construct is simply incompatible with being treated as an actual array.
And you can't give int array[m][n]; global scope because it's variable-length, and global variables are static (unchanging, not necessarily C's static) and have to be fixed-size.
And you can't use something like a typedef to create an "array type", because that's a compile-time definition and the array is "sized" at run time.
So you need to allocate the array dynamically, but the global variable use to access the array won't have sizing information associated with it.
The global variable pretty much has to be a void *.
To access the array, you have to create a pointer at run-time to a VLA and assign the global void * to that pointer.
This would work (note that the local pointer has to be dereferenced to access the array):
#include <stdlib.h>
void *array;
int m;
int n;
// create the array, assumes both
// m and n are already set
void createArray()
{
int ( *localArrayPtr )[m][n] = malloc( sizeof( *localArrayPtr ) );
array = localArrayPtr;
}
void fillArray( void )
{
int ( *localArrayPtr )[m][n] = array;
for ( int ii = 0; ii < m; ii++ )
{
for ( int jj = 0; jj < n; jj++ )
{
( *localArrayPtr )[ ii ][ jj ] = rand();
}
}
}
void someOtherFunc( void )
{
int ( *localArrayPtr )[m][n] = array;
( *localArrayPtr )[ x ][ y ] = ...;
}
As an exercise, it works. Forcing a VLA to have global scope, though, causes accessing the array to be more complex than necessary, adding to existing problems with using globally-scoped variables.
It's probably a lot better from both a scope and code complexity perspective to just pass the array to functions via arguments, such as void someFunc( int m, int n, int array[ m ][ n ] ) { ... }. Accessing elements would then just be in the form array[ x ][ y ].
You could create "getter" and "setter" functions to hide the complexity:
int getElement( int x, int y )
{
int ( *localArrayPtr )[m][n] = array;
return( ( *localArrayPtr )[ x ][ y ] );
}
void setElement( int x, int y, int value )
{
int ( *localArrayPtr )[m][n] = array;
( *localArrayPtr )[ x ][ y ] = value;
}
A good optimizing compiler would likely inline those.
As mentioned by Nate you can create a global pointer and then allocate memory to it using "malloc()"
#include<stdio.h>
#include<stdlib.h>
int *array;
int main() {
int m, n;
scanf("%d", &m);
scanf("%d", &n);
array = malloc(sizeOf(int) * m * n);
return 0;
}
And you can also ref this blog here:

How to get a 2 dimensional array as output of a user defined function?

I am trying to write a user defined function that takes some matrices and variables as inputs and gives a matrix as output. So something like this:
cofactor(int A[100][100], n, r, c){
int B[100][100]
//B becomes the cofactor matrix of A after some operations//
return B;
}
and in my main function I just want to write :
C=cofactor(D, n, r, c);
to turn C into the cofactor matrix of D.
But for some reason c language does not support taking a whole 2D array as output of a function. How can I work around this?
I don't want to keep all the junk in the main function. I want to write a separate function that gives me the matrix as output, and simply call that function in my main function.
Currently in your code B will go out of scope and will be destroyed when control exits cofactor.
Thus use pointer to pointer as below.
int **cofactor(int A[100][100], int n, int r, int c){
int **B = malloc(sizeof(int *)*r);
for (int i =0;i<r;i++)
B[i] = malloc(sizeof(int)*c);
//B becomes the cofactor matrix of A after some operations//
return B;
}
And from main.
int **C=cofactor(D, n, r, c);
Note:: NULL checks are not added and allocated memory needs to be freed once done with the processing.
You are correct in that C doesn't allow us to return arrays from functions. This is one area where C is simply plain bad and you'll find yourself choosing between various evils.
The most obvious alternatives are to return an array pointer, or a void pointer.
void pointers should be avoided since they have non-existent type safety.
// bad code
void* cofactor (int A[100][100], int n, size_t r, size_t c)
The array pointer option is rather ugly-looking, hard to read and enforces fixed-size dimensions:
// bad code
int ( *cofactor (int A[100][100], int n, size_t r, size_t c) )[100][100];
Alternatively, also ugly and bad practice, is to hide the array type behind a typedef:
// bad code
typedef int arr_t [100][100];
arr_t* cofactor(int A[100][100], int n, size_t r, size_t c)
The array pointer versions also have the limit that you can't use variable dimensions. But r and c here seem to be rows and columns, so you probably do want the array to have variable size.
This is where some start to use int** out of confusion. But int** cannot be used to point at a 2D array, nor to the first element of a 2D array. It can be used to point at the first element of a 1D array of int* pointers, and then emulate something that looks like an array, but doesn't behave like one. That's not what you want here either, because it is both slow and dangerous. See Correctly allocating multi-dimensional arrays.
Sigh. So what to use!
If you drop the requirement of "function return ing array" (with emphasis on using return), it turns easier and more flexible. Parameter passing to/from functions in C is most often done through the parameters, and most sound APIs reserve the return value for an error type describing the outcome of the function.
The big advantage here is that when passing an array as parameter, we can use variable dimensions:
void func (size_t r, size_t c, int A[r][c])
Suddenly you can have a function accepting any array size, and somewhat type safe as long as r and c have correct values.
The cleanest is to leave allocation to the caller. Then you get
void func (size_t r, size_t c, int A[r][c], int B[r][c])
Out of all options discussed, this is the only pretty one. But it won't work if the function must do the allocation. Then we must return an array through the parameter. And to that with this syntax, turns a bit ugly too:
void copy (size_t r, size_t c, int (**B)[r][c], int A[r][c])
But if we can live with this strange-looking "pointer to array pointer to an array of int[r][c]", then it solves all problems. It can return an array of variable size from a function to the caller.
A function making a copy of any array and returning it would look like this:
void copy (size_t r, size_t c, int (**B)[r][c], int A[r][c])
{
*B = malloc( sizeof(int[r][c]) );
int (*b)[c] = **B; // pointer to the first row in an array int[r][c]
for(size_t i=0; i<r; i++)
{
for(size_t j=0; j<c; j++)
{
b[i][j] = A[i][j];
}
}
}
Or if you will:
#include <string.h>
void copy (size_t r, size_t c, int (**B)[r][c], int A[r][c])
{
*B = malloc( sizeof(int[r][c]) );
memcpy( *B, A, sizeof(int[r][c]) );
}
Full example:
#include <stdlib.h>
#include <stdio.h>
void copy (size_t r, size_t c, int (**B)[r][c], int A[r][c])
{
*B = malloc( sizeof(int[r][c]) );
int (*b)[c] = **B; // pointer to the first row in an array int[r][c]
for(size_t i=0; i<r; i++)
{
for(size_t j=0; j<c; j++)
{
b[i][j] = A[i][j];
}
}
}
int main (void)
{
int array1[2][3] = { {1,2,3}, {4,5,6} };
int (*array2)[2][3];
copy(2, 3, &array2, array1);
int (*arr)[3] = *array2;
for(size_t i=0; i<2; i++)
{
for(size_t j=0; j<3; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
free(array2);
}

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

Passing multidimensional arrays as function arguments in 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
}

Resources