Pass a two dimensional array to a function of constant parameter - c

I learned from C Primer Plus that if you want to protect an array from being accidentally modified by a function, you should add const modifier before the pointer declaration in the header of function definition.
Following this sensible advice, in the following minimal example, I'm trying to pass a non-constant two-dimensional array array to the function Sum2D, one parameter of which is a pointer-to-const-int[2].
#include <stdio.h>
#define ROWS 2
#define COLS 2
int Sum2D(const int ar[][COLS], int rows); //use `const` to protect input array
int main(void)
{
int array[ROWS][COLS]={{1,2},{3,4}}; //the non-constant array
printf( "%d\n", Sum2D(array,ROWS) );
return 0;
}
int Sum2D(const int ar[][COLS], int rows)
{
int total=0;
int i,j;
for( i=0 ; i<rows ; i++ )
{
for( j=0 ; j<COLS ; j++ )
{
total+=ar[i][j];
}
}
return total;
}
However, gcc cannot successfully compile this code without issuing the following warnings:
$gcc -ggdb3 -Wall -Wextra -o test test.c
test.c: In function ‘main’:
test.c:16:2: warning: passing argument 1 of ‘Sum2D’ from incompatible pointer type [enabled by default]
printf( "%d\n", Sum2D(array,4) );
^
test.c:4:5: note: expected ‘const int (*)[4]’ but argument is of type ‘int (*)[4]’
int Sum2D(const int ar[][COLS], int rows);
^
1) Why the warning?
2) How can I eliminate the "noise"?(Apart from adding const to array declaration.)
(If the array and function both use one-dimensional array, there is no warning.)
System information:
Ubuntu 14.04LTS
Compiler: gcc 4.8.2

This is an unfortunate "bug" in C's design; T (*p)[N] does not implicitly convert to T const (*p)[N]. You will have to either use an ugly cast, or have the function parameter not accept const.
At first sight it looks like this conversion should be legal. C11 6.3.2.3/2:
For any qualifier q, a pointer to a non-q-qualified type may be converted to a pointer to the q-qualified version of the type;
However also look at C11 6.7.3/9 (was /8 in C99):
If the specification of an array type includes any type qualifiers, the element type is so-qualified, not the array type.
This last quote says that int const[4] is not considered to be a const-qualified version of int[4]. Actually it is a non-const-qualified array of 4 const ints. int[4] and int const[4] are arrays of different element types.
So 6.3.2.3/2 does not in fact permit int (*)[4] to be converted to int const (*)[4].
Another weird situation where this issue with const and arrays shows up is when typedefs are in use; for example:
typedef int X[5];
void func1( X const x );
void func1( int const x[5] );
This would cause a compiler error: X const x means that x is const, but it is pointing to an array of non-const ints; whereas int const x[5] means x is not const but it is pointing to an array of const ints!
Further reading here, thanks to #JensGustedt

You can type cast the array while calling the function. It will not automatically convert non-const into const.
You can use this.
Sum2D( (const int (*)[])array, ROWS );

Related

Is an incomplete array type compatible with a complete array type?

Question: are int (*)[] and int (*)[1] compatible?
Demo 1:
typedef int T0;
typedef T0 T1[];
typedef T1* T2[];
T1 x1 = { 13 };
T2 x2 = { &x1 };
GCC, Clang, ICC generate no diagnostics.
MSVC generates:
<source>(21): warning C4048: different array subscripts: 'T1 (*)' and 'T0 (*)[1]'
Note: the T1 (*) is int (*)[], the T0 (*)[1] is int (*)[1].
Demo 2:
int c[][1] = {0};
int (*r)[] = c;
GCC, Clang, ICC generate no diagnostics.
MSVC generates:
<source>(30): warning C4048: different array subscripts: 'int (*)[0]' and 'int (*)[1]'
Extra: C2x, 6.7.6.2 Array declarators, 6:
For two array types to be compatible, both shall have compatible element types, and if both size specifiers are present, and are integer constant expressions, then both size specifiers shall have the same constant value. If the two array types are used in a context which requires them to be compatible, it is undefined behavior if the two size specifiers evaluate to unequal values.
Are int (*)[] and int (*)[1] compatible?
It seems that yes.
C2x, 6.2.7 Compatible type and composite type, 5:
EXAMPLE Given the following two file scope declarations:
int f(int (*)(), double (*)[3]);
int f(int (*)(char *), double (*)[]);
The resulting composite type for the function is:
int f(int (*)(char *), double (*)[3]);
and:
A composite type can be constructed from two types that are compatible; ...
Here we see that double (*)[3] is the composite type constructed from types double (*)[3] and double (*)[].
Hence, the types double (*)[3] and double (*)[] are compatible.
Hence, the types int (*)[] and int (*)[1] are compatible.
I think you want to complicate things by adding identifiers and typedefs.
typedef int T0;
T0 is equivalent to int so let's simplify and use int.
typedef T0 T1[]; ===> int (T1[]); ===> int T1[];
here, T1 is equivalent to an unspecified array type, so let's simplify and use int [].
typedef T1* T2[]; ===> int (*T2[])[];
This is an array of pointers to integer arrays.
Here, T2 is equivalent to an unspecified array type, so let's simplify and use int (*[])[].
when you write
T1 x1 = { 13 }; ===> int x1[] = { 13 }; ===> int x1[1] = { 13 };
as the type is finally completely specified by the initializator, that assigns an array length of one element.
T2 x2 = { &x1 }; ===> int (*x2[])[] = { &x1 };
where x1 is of type int *[1] (and so &x1 is of type int **[1]. This will fix the inner array dimension:
... ===> int (*x2[1])[];
and, as there's only one initializer, this will fix also the outer array dimension:
... ===> int (*x2[1])[1];
So finally x2 results in an array of one pointer to array of one int.
I'd suggest you to simplify your examples, to be able to understand incomplete type definitions yourself, by using only one incomplete type, and not three levels of them. This way you would probably be capable of solving the problem without help.

How C manages the memory

The answer should be 1 and 1. But why the result is 1 and 2. Arr is not a global variable.
#include <stdio.h>
void add (int arr[1])
{
arr[0] = arr[0]+1;
}
int main() {
int arr[1]={1};
printf("%d\n",arr[0]);
add(arr);
printf("%d", arr[0]);
return 0;
}
The answer should be 1 and 1
No, it should be 1 and 2.
void add (int arr[1]) creates a local parameter variable named arr, to which you pass the variable also named arr in the caller.
There are two rules in C informally called "array decay", which means that whenever an array is used in an expression or function parameter declaration, it gets adjusted implicitly by the compiler into a pointer to the first element of the array.
So add(arr); is 100% equivalent to add(&arr[0]);.
And the function defintion void add (int arr[1]) is equivalent to void add (int* arr). This always happens, you can't pass arrays by value in C even though void add (int arr[1]) looks like it does that.
For this reason, the arr in your function points at the arr in the caller. And when you update the contents pointed at, you update the contents of the variable in main.
When an array is declared as a function argument, it becomes a pointer. That's just the way it is. Here is a snippet with corresponding errors ans warnings that proves it:
$ cat main.c
void foo(int arg[10]) {
int arr[10];
float *x;
arg = x;
arr = x;
}
$ gcc -c main.c
main.c: In function ‘foo’:
main.c:5:9: warning: assignment to ‘int *’ from incompatible pointer type ‘float *’ [-Wincompatible-pointer-types]
5 | arg = x;
| ^
main.c:6:9: error: assignment to expression with array type
6 | arr = x;
| ^
So in this case, arr is an array, but arg is not. This means that your add function is equivalent to this:
void add (int *arr)
{
arr[0] = arr[0]+1;
}
And when you pass an array to a function, it will automatically decay to a pointer. So the output 1 and 2 is the expected output.

Void** as parameter requires cast

I have a function in C that needs to receive a pointer to an array (with an unspecified type).
To do so, I use void**, as I would use void* to receive an array of unspecified elements.
There's a problem unfortunately: the compiler gives a warning (passing argument 1 of 'f' from incompatible pointer type). If I ignore the warning, and try to execute the program, everything works as expected.
The only way to get rid of the warning is to cast whatever I try to pass to the function to void**.
Why does C behaves like that? And is there a better way to solve the warning?
PS: I need to compile using GCC with the flags -std=gnu89 -pedantic -Wall
Example
int f(void** param){ return 1; }
int main(){
int *arr = malloc(sizeof(int) * 20);
int i;
for(i=0; i < 20; i++) arr[i] = i;
f(&arr);
}
The pointer to anything type is void*, and the compiler will not complain about conversions to that type. But void** is not a pointer to anything, it's a pointer to an array of pointers to anything, which is quite different from a pointer to an array of pointers to integers, so the compiler complains.
So, to solve the warning, yes you would need to cast explicitly.
Although void * is the "generic pointer" in C, void ** isn't a "generic pointer to pointer". Instead, it's nothing more than the "specific pointer to void *, the generic pointer".
In your case, a int ** is converted implicitly to a void **, which is not generic. Since a void ** is not guaranteed to be able to hold all pointer variables (thus incompatible to a int **), the compiler raises a warning.
Here is the warning generated by clang:
main.c:7:7: warning: incompatible pointer types passing 'int **' to parameter of
type 'void **' [-Wincompatible-pointer-types]
f(&arr);
^~~~
main.c:1:14: note: passing argument to parameter 'param' here
int f(void** param){ return 1; }
To eliminate this warning, you can have int f(void* param);, and cast param to int ** inside the function. There will be no warning because a void * can be used to store any pointer (Quoted from N1570):
6.3.2.3 Pointers
1 A pointer to void may be converted to or from a pointer to any
object type. A pointer to any object type may be converted to a
pointer to void and back again; the result shall compare equal to the
original pointer.
It seems that you want to modify the address of the data (not the value) inside the function, you can't do that directly with a void * because you can't use arithmetic with void *, but you can pass the size of the first element and a chunk of bytes (char *), suppose you want to change the address of arr to arr + 1 (the second element of the array) inside the function:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void f(void *ptr, size_t size)
{
// char *p = *ptr; Wrong, you can't dereference a void * without a cast
char *p = *(char **)ptr; /* pointer to address of ptr */
memmove(p, p + size, size); /* assign the address of ptr + 1 to ptr */
}
int main(void)
{
int *arr = malloc(sizeof(int) * 20);
int i;
for (i = 0; i < 20; i++) arr[i] = i;
f(&arr, sizeof arr[0]);
printf("%d\n", arr[0]);
return 0;
}
Output:
1

How to pass a const multi-dimension array to a function? [duplicate]

I learned from C Primer Plus that if you want to protect an array from being accidentally modified by a function, you should add const modifier before the pointer declaration in the header of function definition.
Following this sensible advice, in the following minimal example, I'm trying to pass a non-constant two-dimensional array array to the function Sum2D, one parameter of which is a pointer-to-const-int[2].
#include <stdio.h>
#define ROWS 2
#define COLS 2
int Sum2D(const int ar[][COLS], int rows); //use `const` to protect input array
int main(void)
{
int array[ROWS][COLS]={{1,2},{3,4}}; //the non-constant array
printf( "%d\n", Sum2D(array,ROWS) );
return 0;
}
int Sum2D(const int ar[][COLS], int rows)
{
int total=0;
int i,j;
for( i=0 ; i<rows ; i++ )
{
for( j=0 ; j<COLS ; j++ )
{
total+=ar[i][j];
}
}
return total;
}
However, gcc cannot successfully compile this code without issuing the following warnings:
$gcc -ggdb3 -Wall -Wextra -o test test.c
test.c: In function ‘main’:
test.c:16:2: warning: passing argument 1 of ‘Sum2D’ from incompatible pointer type [enabled by default]
printf( "%d\n", Sum2D(array,4) );
^
test.c:4:5: note: expected ‘const int (*)[4]’ but argument is of type ‘int (*)[4]’
int Sum2D(const int ar[][COLS], int rows);
^
1) Why the warning?
2) How can I eliminate the "noise"?(Apart from adding const to array declaration.)
(If the array and function both use one-dimensional array, there is no warning.)
System information:
Ubuntu 14.04LTS
Compiler: gcc 4.8.2
This is an unfortunate "bug" in C's design; T (*p)[N] does not implicitly convert to T const (*p)[N]. You will have to either use an ugly cast, or have the function parameter not accept const.
At first sight it looks like this conversion should be legal. C11 6.3.2.3/2:
For any qualifier q, a pointer to a non-q-qualified type may be converted to a pointer to the q-qualified version of the type;
However also look at C11 6.7.3/9 (was /8 in C99):
If the specification of an array type includes any type qualifiers, the element type is so-qualified, not the array type.
This last quote says that int const[4] is not considered to be a const-qualified version of int[4]. Actually it is a non-const-qualified array of 4 const ints. int[4] and int const[4] are arrays of different element types.
So 6.3.2.3/2 does not in fact permit int (*)[4] to be converted to int const (*)[4].
Another weird situation where this issue with const and arrays shows up is when typedefs are in use; for example:
typedef int X[5];
void func1( X const x );
void func1( int const x[5] );
This would cause a compiler error: X const x means that x is const, but it is pointing to an array of non-const ints; whereas int const x[5] means x is not const but it is pointing to an array of const ints!
Further reading here, thanks to #JensGustedt
You can type cast the array while calling the function. It will not automatically convert non-const into const.
You can use this.
Sum2D( (const int (*)[])array, ROWS );

What is the difference between *p and (*p)[3] in the function?

I'm new in programming and learning pointers in array in C. Have a look at the below programmes.
1st program
#include<stdio.h>
int fun();
int main()
{
int num[3][3]={21,325,524,52,0,6514,61,33,85};
fun(num);
printf("%d",*(*(num+1)+1));
*(*(num+1)+1)=0;
printf("%d",*(*(num+1)+1));
return 0;
}
int fun(int **p)
{
*(*(p+1)+1)=2135;
return 0;
}
2nd program
#include<stdio.h>
int fun();
int main()
{
int num[3][3]={21,325,524,52,0,6514,61,33,85};
fun(num);
printf("%d",*(*(num+1)+1));
*(*(num+1)+1)=0;
printf("%d",*(*(num+1)+1));
return 0;
}
int fun(int *p)
{
*((p+1)+1)=2135;
return 0;
}
3rd program
#include<stdio.h>
int fun();
int main()
{
int num[3][3]={21,325,524,52,0,6514,61,33,85};
fun(num);
printf("%d",*(*(num+1)+1));
*(*(num+1)+1)=0;
printf("%d",*(*(num+1)+1));
return 0;
}
int fun(int (*p)[3])
{
*(*(p+1)+1)=2135;
return 0;
}
In the first program **p is used in the fun() function which I think it should be correct and in that function I've written *(*(p+1)+1) to change the first element of first array. But on compiling this program it's showing error: invalid type argument of unary '*' (have 'int'). As far as I know num is a pointer to array and it is holding the address of num[1] which is again holding the address of num[1][0].
On compiling the second program compiler is showing no error. And *((p+1)+1)=0 is changing the value of 2nd element of first array. Why it is changing the value of 2nd element of zeroth array not the value of first element of first array? and How? It should be *(*(p+1)+1)=0.
In the third program the compler is showing no error and it is showing the correct result. How?. What does *(p)[3] mean?
I had searched about this but couldn't found the satisfactory result.
All of your programs are ill-formed. Your compiler must produce warning or error messages, and the output of any executable produced is meaningless.
They are ill-formed because int[3][3] is not compatible with int **, nor with int *, nor with int *[3].
To pass int[3][3] to a function, the function must accept int (*)[3] and nothing else (well, except for void *).
This is because arrays can be converted to a pointer to the first element of the array. (In C syntax, num can be used to mean &num[0]).
In C, there are only truly one-dimensional arrays; an array of type int[3][3] is considered to be an array of 3 elements, each of which is an array of 3 ints.
So a pointer to the first element of num is a pointer to an array of 3 ints, which is written as int (*p)[3]. You could write:
int (*p)[3] = &num[0];
or the shorthand for the same thing:
int (*p)[3] = num;
NB. You continually write *(*(num+1)+1)) which is difficult to read. Instead of this, num[1][1] seems much clearer.
In C, x[y] is always exactly equivalent to *(x+y).
I think you are asking: What's the difference between
int fun(int *p)
and
int fun(int (*p)[3])
The first one expects a pointer to an int. The second one expects a pointer to an array of 3 ints.
You are able to call to both these functions using num since you declared the function as
int fun();
If you declare the functions like they are defined, you will get compiler error/warning for the first version.
Here's an updated version of your code and the resulting compiler warning, using gcc and compiler flag -Wall.
#include <stdio.h>
int fun(int *p);
int main()
{
int num[3][3]={21,325,524,52,0,6514,61,33,85};
fun(num);
return 0;
}
int fun(int *p)
{
*(p+0)=2135;
return 0;
}
test.c: In function ‘main’:
test.c:7:4: warning: missing braces around initializer [-Wmissing-braces]
test.c:7:4: warning: (near initialization for ‘num[0]’) [-Wmissing-braces]
test.c:8:4: warning: passing argument 1 of ‘fun’ from incompatible pointer type [enabled by default]
test.c:3:5: note: expected ‘int *’ but argument is of type ‘int (*)[3]’

Resources