Pass multiple-dimensional array to a function in C - c

I have a function like this:
void myfunc(int** arr, int n) {
int i, j;
for(i=0; i<n; ++i) {
for(j=0; j<n; ++j) {
printf("%d,", *(arr + i*n + j) ); // Print numbers with commas
}
printf("\n"); // Just breakline
}
}
in other function I have an two-dimensional array like this:
int main() {
int seqs[8][8] = {
{0, 32, 36, 52, 48, 16, 20, 4},
{0, 16, 20, 52, 48, 32, 36, 4},
{0, 32, 36, 44, 40, 8, 12, 4},
{0, 8, 12, 44, 40, 32, 36, 4},
{0, 32, 36, 38, 34, 2, 6, 4},
{0, 2, 6, 38, 34, 32, 36, 4},
{0, 32, 36, 37, 33, 1, 5, 4},
{0, 1, 5, 37, 33, 32, 36, 4}
};
// Call to myfunc
myfunc(seqs, 8); // This is the idea
}
But compiler throw me this error:
lab.c: In function 'main':
lab.c:75:5: warning: passing argument 1 of 'myfunc' from incompatible pointer type [enabled by default]
lab.c:4:6: note: expected 'int **' but argument is of type 'int (*)[8]'
What is the right way to pass this array (seqs) to function (myfunc)?

In C99 or C11, you would do it like this:
void myfunc(int n, int arr[n][n])
{
for (int i = 0; i < n; ++i)
{
for (int j = 0; j < n; ++j)
printf("%d,", arr[i][j]);
printf("\n");
}
}
Note that the size precedes, not follows, the array. This function will work correctly with:
int main(void)
{
int seqs[8][8] =
{
{ 0, 32, 36, 52, 48, 16, 20, 4 },
{ 0, 16, 20, 52, 48, 32, 36, 4 },
{ 0, 32, 36, 44, 40, 8, 12, 4 },
{ 0, 8, 12, 44, 40, 32, 36, 4 },
{ 0, 32, 36, 38, 34, 2, 6, 4 },
{ 0, 2, 6, 38, 34, 32, 36, 4 },
{ 0, 32, 36, 37, 33, 1, 5, 4 },
{ 0, 1, 5, 37, 33, 32, 36, 4 },
};
myfunc(8, seqs);
int matrix3x3[3][3] = { { 1, 2, 3 }, { 2, 4, 6 }, { 3, 6, 9 } };
myfunc(3, matrix3x3);
}
I was asked:
Your example does look much better indeed, but is it well-defined? Is n really guaranteed to be evaluated before int arr[n][n]? Wouldn't the order of evaluation of function parameters be unspecified behavior?
The old standard (ISO/IEC 9899:1999) says in §6.7.5.2*Array declarators*:
¶5 If the size is an expression that is not an integer constant expression: if it occurs in a
declaration at function prototype scope, it is treated as if it were replaced by *; otherwise,
each time it is evaluated it shall have a value greater than zero. The size of each instance
of a variable length array type does not change during its lifetime. Where a size
expression is part of the operand of a sizeof operator and changing the value of the
size expression would not affect the result of the operator, it is unspecified whether or not
the size expression is evaluated.
And it gives an example (it is non-normative text because it is an example, but strongly indicative of what is expected):
EXAMPLE 4 All declarations of variably modified (VM) types have to be at either block scope or
function prototype scope. Array objects declared with the static or extern storage-class specifier
cannot have a variable length array (VLA) type. However, an object declared with the static storage class
specifier can have a VM type (that is, a pointer to a VLA type). Finally, all identifiers declared with a
VM type have to be ordinary identifiers and cannot, therefore, be members of structures or unions.
extern int n;
int A[n]; // invalid: file scope VLA
extern int (*p2)[n]; // invalid: file scope VM
int B[100]; // valid: file scope but not VM
void fvla(int m, int C[m][m]); // valid: VLA with prototype scope
void fvla(int m, int C[m][m]) // valid: adjusted to auto pointer to VLA
{
typedef int VLA[m][m]; // valid: block scope typedef VLA
struct tag {
int (*y)[n]; // invalid: y not ordinary identifier
int z[n]; // invalid: z not ordinary identifier
};
int D[m]; // valid: auto VLA
static int E[m]; // invalid: static block scope VLA
extern int F[m]; // invalid: F has linkage and is VLA
int (*s)[m]; // valid: auto pointer to VLA
extern int (*r)[m]; // invalid: r has linkage and points to VLA
static int (*q)[m] = &B; // valid: q is a static block pointer to VLA
}
There are other examples showing variably modified function parameters.
Also, in §6.9.10 Function definitions, it says:
¶10 On entry to the function, the size expressions of each variably modified parameter are
evaluated and the value of each argument expression is converted to the type of the
corresponding parameter as if by assignment. (Array expressions and function
designators as arguments were converted to pointers before the call.)

Arrays and pointers are not the same. Similarly, two-dimensional array and pointer to pointer are not the same thing. The type of seqs mismatches function argument, you can modify the signature of myfunc to:
void myfunc(int arr[][8], int n)
or, you can modify seqs to a real pointer to pointer.

You're declaring the int** as an array with a static size, so its signature is int *[8]. You can get rid of the compiler error by casting the array when you call myfunc.
myfunc((int **)seqs, 8);
In an actual program such an array would most likely be generated dynamically, and you wouldn't have to do this.

The way you're accessing the value from the array in your function, would give you the right element with a simple int * rather than an int **. With *(arr + i*n + j). The i*n gets you to the right row, and the j gets you the column. So change your function header line to:
void myfunc(int* arr, int n) {
...(with one '*'), and pass the address of the first element rather than the bare array name, like this:
myfunc(&seqs[0][0], 8);
..or the first row:
myfunc(seqs[0], 8);

When you define a 2-dimensional array as
int seqs[8][8];
the type of seqs is array of 8 elements where each element is of type array of 8 integers. When you pass seqs to myfunc, seqs is implicitly converted to a pointer to its first element (which is what happens when you pass an array to a function), i.e., to type * element. element is of type int [8]. Hence seqs is implicitly converted to type int *[8] - a pointer to an array of 8 integers.
In your function myfunc, the type of the parameter arr is int ** clearly a different type than what you pass it when you call it in main. They are different types and have different pointer arithmetic. The compiler complains as a result.
You should change the type of arr to int *[8].
void myfunc(int arr[][8], int n);
arr is not an array here but a pointer to an array of 8 integers and you might as well declare myfunc as
void myfunc(int *arr[8], int n);
They are exactly the same.
Edit
Regarding your comment here, you cannot do the following and expect things to work correctly.
// in main
myfunc((int **)seqs, 8);
// the above call is essentially doing this
int **arr = (int **)seqs;
seqs and arr are incompatible types and typecasting seqs suppresses the warning emitted otherwise by the compiler which is worse because typecasting doesn't correct things, and make it seem as if things are alright.
Let us see why. (After the function call.)
seqs (type: array of 8 int[8] type; value: address of the location seqs[0][0])
*seqs (type: int[8] type, value: address of the location seqs[0][0])
arr (type: pointer to a pointer to an int; value: address of the location seqs[0][0])
*arr (type: pointer to an int; value: seqs[0][0])
**arr (type: int; value: value at the address seqs[0][0])
The above assertions can be checked with the assert macro. So we see that when we do **arr what we are actually doing is treating the value seqs[0][0] as a memory address and trying to access the value at that location. This is obviously wrong! It can lead to undefined behaviour or most likely segmentation fault.
There's no way to make the value of seqs (value it evaluates to in the initialization of arr) act like a int ** even by typecasting. This also shows that we should be careful with typecasting values and shouldn't do it unless we know and are sure of what we are doing.
Therefore, you must change your myfunc function signature to do what you want.

Related

why I am not able to initialize array of pointers providing 2d array

int *p = ( int[] ){ 1, 2, 3, 4 };
Doing this I am able to initialize a anonymous array to a pointer p.
In a similar way I want to assign arrays of array (i.e. probably 2D array) in
array of pointers. And so I have tried the following code:
int *p[]= (int [][3]) { {1,2,3},{10,20,30} };
But it goes wrong, anyway if I assign it to a pointer to whole Array( as int (*p)[]= (int [][3]) { {1,2,3},{10,20,30} }; ) it works fine.
I am in confused that if a pointer can get assigned a anonymous array why the array of pointers could not get assigned to 2d array?
The compound literal
(int [][3]) { {1,2,3},{10,20,30} };
has the type
int [2][3]
i.e. array of 2 elements, each consisting of 3 int elements.
When used in the line
int *p[]= (int [][3]) { {1,2,3},{10,20,30} };
the array will decay to a pointer to the first element. This pointer will therefore have the type "pointer to array of 3 int elements`.
However, you cannot assign this data type to p, because the types are different. You declared p as an array of pointers. That is why your code is not working.
If you want p to be an array of pointers in which every pointer points to its array of int elements, then you will need to use several compound literals, one for each of these arrays:
int *p[] = {
( int[] ){ 1, 2, 3, 4 },
( int[] ){ 5, 6, 7, 8 },
( int[] ){ 9, 10, 11, 12 }
};
Here is a small test program:
#include <stdio.h>
int main( void )
{
int *p[] = {
( int[] ){ 1, 2, 3, 4 },
( int[] ){ 5, 6, 7, 8 },
( int[] ){ 9, 10, 11, 12 }
};
printf( "%d\n", p[1][2] );
}
This program has the output 7.
EDIT:
According to your remarks in the comments section of this answer, it seems that you have several 2D arrays and want to create an array of pointers to these arrays. In that case, you can do the following:
#include <stdio.h>
int main( void )
{
//define first 2D array
int arr1[][4] = {
{ 1, 2, 3, 4},
{ 5, 6, 7, 8},
{ 9, 10, 11, 12}
};
//define second 2D array
int arr2[][4] = {
{ 13, 14, 15, 16},
{ 17, 18, 19, 20},
{ 21, 22, 23, 24}
};
//define array of 2 pointers that point to the first
//rows of arr1 and arr2
int (*p[])[4] = { arr1, arr2 };
//use pointer array to get the 4th element in the
//3rd row of the second 2D array
printf( "%d\n", p[1][2][3] );
}
This program will give you the correct output 24.
If you find the declaration int (*p[])[4] hard to understand, then you may want to read this page on how these declarations are to be interpreted. Also, this site may be useful.
You can also simplify your declarations by using a typedef, so that the type arrow_row represents an array of 4 int elements (i.e. a row of a 2D array) like this:
typedef int array_row[4];
Now, you can simplify the declaration
int (*p[])[4] = { arr1, arr2 };
to:
array_row *p[] = { arr1, arr2 };
int *p[] is an array of pointers, not a pointer to an array.
You'll need to do:
int (*p)[3]= (int [][3]) { {1,2,3},{10,20,30} };
The reason why is that an array, whenever used in an expression, decays into a pointer to the first element. So no matter the array type, your pointer will need to correspond to the pointer to the first element.
In case of an int[], the element type is int and a pointer to it would be int*.
In case of an int[][3], the element type is int[3] and a pointer to it would be int (*p)[3].

How to pass matrix to function by reference in C?

I'm defining a matrix, A, and I just want to print it out:
#include <stdio.h>
#define N 4
double A[N][N]= {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12},
{13, 14, 15, 16}
};
void print_matrix(double **A) {
int i, j;
for(i = 0; i < N; i++) {
for(j = 0; j < N; j++) {
printf("%f ", A[i][j]);
}
printf("\n");
}
}
int main() {
print_matrix(A);
}
But on compile I get the error: expected 'double **' but argument is of type 'double (*)[4]'
I tried in the main function to pass the matrix like print_matrix(&A); but then the error was expected 'double **' but argument is of type 'double (*)[4][4]'
Pointer-to-pointer has nothing to do with multi-dimensional arrays. Simply declare the function as void print_matrix(double A[N][N]).
Thanks to "array decay", this passes the array by reference, since double A[N][N] when part of a parameter list, gets implicitly "adjusted" into a pointer to the first element, double (*A)[N].
There are multiple ways of defining a multidimensional array in C - and they have different exact semantics and behaviors w.r.t. passing to functions.
The approach you chose actually defines a single contiguous sequence of elements, which the double-bracketed access simply calculates an index into; it doesn't actually go through an array of pointers.
But you could also create an array of pointers and a large purely-unidimensional array for the entire data. See C FAQ 6.16.

Initialize an array using the pointer to an existing array

I have already understood that one can't assign arrays to arrays in c. A statement like:
int array1[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int array2[10];
int * array3 = array;
Is valid, but a consecutive statement:
array2 = array1;
Is not, because arrays do decay to pointers. I find this not satisfying, since in case I have a function that creates an array:
int * arrcreate() {
static int funcarray[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
return funcarray
}
And I call it in my main routine, I'd like to handle an array, not a pointer.
Of course I could in my main program create an array and fill it by hand:
int main() {
int array[10];
int i;
int * p = arrcreate();
for(i = 0; i<10, i++) {
array[i] = p[i];
}
}
But since I KNOW that when initializing an array it is possible to do kind of an assignment:
int array1[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
I wanted to ask if it is possible to assign an array with the pointer to an array as well.
An equivalent question would be: Given a pointer to an array, and knowing the size of the array the pointer is pointing to, is it possible to create and initialize on the fly a new array, which is in every matter a copy of the old one?
As Bathsheba said, an array cannot be on the left hand side of an assignment like the one you put in your question. But if you will always know both the pointer to the array you would like to copy, as well as the array size, you could write a function along the lines of:
void copy_array(const int *a, const int arraySize, int **b) {
*b = malloc(arraySize * sizeof(*a));
for (int indx = 0; indx < arraySize; indx++) {
(*b)[indx] = a[indx];
}
}
int main() {
int array1[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int *array2;
copy_array(array1, &array2);
// do stuff
free(array2);
}
Is that an option?
Unfortunately the answer is not. The draft n1570 for C11 says in 6.7.9 Initialization:
...16 Otherwise, the initializer for an object that has aggregate or union type shall be a brace enclosed
list of initializers for the elements or named members.
(the previous items concern struct, union or character type array)
That means that an int array can only be initialized with a brace enclosed list of integer values. And a pointer to an array does not follow that definition.
Informally speaking, an array cannot be an lvalue. This means that an array cannot be on the left hand side of an assignment.
Note secondly that int array1[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; is not an assignment but is array initialisation.
If you're careless regarding terminology, the two sentences appear to be in contradiction. But they are not; furthermore you can see that it is not possible to initialise an array using the values pointed at by a particular pointer. You need to use something on the lines of memcpy.

Pointer to pointer dereference with pre-increment operator

The following program gives output as 17,29,45; I can't understand what does **++pp; mean. Can anyone explain the program in detail.
#include <stdio.h>
int main() {
static int a[] = {10, 22, 17, 29, 45};
static int *p[] = {a, a + 2, a + 1, a + 4, a + 3};
int **pp = p;
**++pp;
printf("%d %d %d", **pp, *pp[3], pp[0][2]);
}
In your code, **++pp; is the same as * (* ( ++pp));. It first increments the pointer, then deferences twice (the first dereference result is of pointer type, to be elaborate).
However, the value obtained by dereferencing is not used. If you have compiler warnings enabled, you'll get to see something like
warning: value computed is not used
You can remove the dereferencing, it's no use.

pointer to array and 2D array representation

I have doubt in syntax of pointer to array and 2D array
#include<stdio.h>
#include<stdlib.h>
void show ( int q[][4], int row )
{
int i, j ;
for ( i = 0 ; i < row ; i++ )
{
for ( j = 0 ; j < 4 ; j++ )
printf ( "%d ", q[i][j] ) ;
printf ( "\n" ) ;
}
printf ( "\n" ) ;
}
int main( )
{
int a[][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 0, 1, 6}
} ;
show ( a, 3, 4 ) ;
return 0;
}
The above code works with all the below notations
void show ( int q[][4], int row )
void show ( int q[3][4], int row )
void show ( int ( *q )[4], int row, int col )
int q[][4] == int q[3][4]==int ( *q )[4] // represents pointer to 1D array with size 4
I have read this
int q[ ][4];
This is same as int ( *q )[4], where q is a pointer to an array of 4
integers. The only advantage is that we can now use the more
familiar expression q[i][j] to access array elements.
Question:
The show() function works with both of these, int q[][4] and int q[3][4] to receive 2D array's address.
But these are representation for 2D array right?
we can't allocate 2D like the following
int (*a)[4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 0, 1, 6}
} ;
But 2D can be allocated via this statement
int a[][4]={
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 0, 1, 6}
} ;
int (*a)[4] is not the same as int a[][4], then how come both 'pointer to array' and '2D array notation' can be used in show function?
Arrays are not pointers, but in many situations, arrays decay into pointers, for example when passed as function arguments. So when you pass an int q[][4] to a function, the function receives an int (*q)[4], a pointer to arrays of four int. That's why both forms work as arguments to show(). But for the declaration and initialization, the real type matters, so you can't initialize a pointer with an array-initializer.
Functions have special rules regarding array parameters, the basic rule is if you speak the function's prototype, and your first word is array, you change it to "pointer to", so that
(int foo[5]) ==> array[5] of int ==> pointer to int
(int foo[][4]) ==> array[] of array[4] of int ==> pointer to array[4] of int
(int (*foo)[4]) ==> pointer to array[4] of int ==> pointer to array[4] of int
(int *argv[]) ==> array of pointer to int ==> pointer to pointer to int
Within a function, that conversion doesn't happen, and there is a big difference between array and pointer types.
int a[] = {5, 3, 2};
Is legal, but
int *a = {5, 3, 2};
is not. As a rule, you can't assign a pointer from an array initializer. Instead, c99 provides compound literals:
int *a = (int[]){5, 3, 2};
int (*a)[4] = (int [][4]){
{2, 2, 3, 1},
{2, 3, 5, 3},
};
First, some language from the C standard:
6.3.2.1 Lvalues, arrays, and function designators
...
3 Except when it is the operand of the sizeof operator or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue. If the array object has register storage class, the behavior is undefined.
In the call to the show function, the expression a has type "3-element array of 4-element array of int". By the rule above, this expression is replaced with an expression of type "pointer to 4-element array of int" before the call is made; thus, show receives a pointer value of type int (*)[4], not an array.
More standard language:
6.7.5.3 Function declarators (including prototypes)
...
7 A declaration of a parameter as ‘‘array of type’’ shall be adjusted to ‘‘qualified pointer to type’’, where the type qualifiers (if any) are those specified within the [ and ] of the array type derivation. If the keyword static also appears within the [ and ] of the array type derivation, then for each call to the function, the value of the corresponding actual argument shall provide access to the first element of an array with at least as many elements as specified by the size expression.
Thus, in the context of a function parameter declaration, T a[n], T a[], and T *a are all equivalent. If T is an array type, such as "4-element array of int", then int a[3][4], int a[][4], and int (*a)[4] are all equivalent.
Note that this is only true for function parameter declarations. When you're declaring an array object, the following language comes into play:
6.7.8 Initialization
...
22 If an array of unknown size is initialized, its size is determined by the largest indexed element with an explicit initializer. At the end of its initializer list, the array no longer has incomplete type.
So when you write
int a[][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 0, 1, 6}
} ;
you are declaring an Nx4 array of int, not a pointer to an array of int -- the inner dimension 3 is inferred from the number of 4-element arrays in the initializer.

Resources