Passing a 2D array to a function taking a pointer - c

#include <stdio.h>
void func(int **);
int main(void)
{
int ptr[2][3] = { {8, 7, 3}, {4, 5, 6} };
printf("ptr point is %p\n", ptr);
printf("*ptr point is %p\n", *ptr);
func(ptr);
system("pause");
return 0;
}
void func(int *ptr[8])
{
printf("ptr point is %p\n", ptr);
printf("*ptr point is %p\n", *ptr);
printf("*(ptr + 1) point is %p\n", *(ptr + 1));
}
Output:
ptr point is 004FFC24
*ptr point is 004FFC24
ptr point is 004FFC24
*ptr point is 00000008
*(ptr + 1) point is 00000007
Press any key to continue
Why ptr now becomes a 1D pointer?
As you can see
*(ptr) in func() output a 8;
*(ptr + 1) in func() output a 7;they are all digits in the array,
However, ptr should be 2D pointer, (because [] match with *, consequently *ptr[8] should match with **ptr).
so *(ptr + 1) and *(ptr) should be 1D pointers, instead of digits?

You have several issues.
First, you declare func to take a char **, but later define it to take int *[8], which doesn’t match. Even worse, both are wrong - or rather, they don’t match the type of the argument you’re passing.
Except when it is the operand of the sizeof or unary & operators, an expression of type “N-element of T” will be converted (“decay”) to an expression of type “pointer to T”, and the value of the expression will be the address of the first element.
When you pass ptr to func, it is converted from type “2-element array of 3-element array of int” to “pointer to 3-element array of int”, or int (*)[3]. So the function prototype needs to be
void func( int (*ptr)[3] )
or
void func( int ptr[][3] )
Because your function definition and declarations don’t match, and because neither matches the actual type of the argument, you get the unexpected output. You should have gotten a warning on the mismatched function argument types; if not, you may need to raise the warning level.

Related

Array and pointer (something like &arr)

#include <stdio.h>
int main()
{
int *a, *b,*c,**d;
int arr[10]={0};
a=arr;
b=&arr;//is this true?why?
c=&arr[0];
d=&a;
printf("%p,%p,%p,%p",a,b,c,d);
return 0;
}
Does &arr something point to the first address of the array?
Is b=&arr; true?
What's the difference between arr and &arr?
Does &arr something point to the first address of the array?
&arr is pointer to whole array of 10 integers and not the pointer to first element of the array.
Is b=&arr; true?
No, given int *b, then b=&arr; is an incompatible pointer type assignment because the type of &arr is int (*)[10] whereas the type of b is int *. (If the definition is int (*b)[10], there is no problem.)
what is the difference between arr and &arr?
The arr, when used in a statement, will be converted to pointer to first element of array (there are few exceptions to this rule). The type of arr is int * and it is equivalent to &arr[0]1) (i.e. both are pointer to first element of array).
The &arr is pointer to the whole array (it's type is int (*)[10]).
The address of an array (&arr) and address of first element of an array (arr or &arr[0]) is numerically same though their type is different. That means, if we add 1 to them there results will be different (provided that the array size is greater than 1). arr + 1 result in pointer to 2nd element whereas &arr + 1 result in pointer after 10 elements of array arr.
Demonstration:
#include<stdio.h>
int main (void) {
int arr[10];
printf ("arr - %p\n", (void *)arr);
printf ("&arr - %p\n", (void *)&arr);
printf ("arr + 1 - %p\n", (void *)(arr + 1));
printf ("&arr + 1 - %p\n", (void *)(&arr + 1));
return 0;
}
Output:
# ./a.out
arr - 0x7ff7bec77950
&arr - 0x7ff7bec77950
arr + 1 - 0x7ff7bec77954
&arr + 1 - 0x7ff7bec77978
Also, when using the format specifier %p in printf(), the corresponding argument('s) should be type casted to (void *).
1). From C11 Standards#6.5.2.1
The definition of the subscript operator [] is that E1[E2] is identical to (*((E1)+(E2)))..
Hence,
arr -> (arr + 0) -> &( *(arr + 0) ) -> &arr[0]

A pointer to an address of an array

Why does this code output: 1 ≡ arr[0] and not &arr[0]?
My assumption is that after the assignment, ptr holds the address of &arr, which is a pointer to the first element of arr or arr[0].
So dereferencing the ptr should yield the value stored at that address, which is the memory location of the first element.
#include<stdio.h>
int main(void) {
int arr[] = { 1 };
int* ptr = &arr;
printf("%d\n", *ptr);
return 0;
}
A couple of things...
First, a doesn't store the location of a[0]. There is no object a that is separate from the array element a[0]. Basically what you have in memory is
Address
------- +------+
0x1000 a: | 0x01 | a[0]
+------+
In other words, the address of an array is the same as the address of its first element.
Unless it is the operand of the sizeof or unary & operators, the expression a will be converted ("decay") from type "1-element array of int" (int [1]) to "pointer to int" (int *) and the value of the expression will be the address of the first element in the array.
This means that the expressions &a, a, and &a[0] all yield the same address value; it's just the types of the expressions are different:
Expression Type Decays to
---------- ---- ---------
&a int (*)[1]
a int [1] int *
&a[0] int *
Which brings us to this line:
int* ptr = &arr; // int * = int (*)[1] - assignment of incompatible types
The compiler should have yelled at you about that line. You may want to dial up the warning level.
I added some print statements to your code:
#include<stdio.h>
int main(void) {
int arr[] = { 1 };
int *ptr = (int *)arr;
printf("%p\n", (void *)arr);
printf("%p\n", (void *)&arr);
printf("%p\n", (void *)&arr[0]);
printf("%p\n", (void *)ptr);
printf("%p\n", (void *)&ptr);
printf("%d\n", *ptr);
return 0;
}
It prints:
0x7ffe3fe37aa4
0x7ffe3fe37aa4
0x7ffe3fe37aa4
0x7ffe3fe37aa4
0x7ffe3fe37a98
1
Which means arr and ptr are on the stack: ptr is at 0x7ffe3fe37a98 and has the value 0x7ffe3fe37aa4, which is where the arr is stored.
Note that arr and &arr have the same value. See this question: How come an array's address is equal to its value in C?
My assumption is that after the assignment, ptr holds the address of &arr, which is a pointer to the first element of arr or arr[0].
That is correct.
So dereferencing the ptr should yield the value stored at that address
Also correct.
which is the memory location of the first element.
No. Since ptr is pointing to the first element, dereferencing yields the first element, which is 1.

Pointer to an entire array

I stumbled upon "pointer to the entire array" and wrote a test program to clear some things out:
#include <stdio.h>
int main(){
int x[5] = {1, 2, 3, 4, 5};
int *a = x; // points to the 1st array element
int (*b)[5] = &x; // points to the 1st addres of the stream of 5 int elements
int (*c)[5] = x; // points to the 1st addres of the stream of 5 int elements
printf("%p\n%p\n%p\n\n",
(void *)a,
(void *)b,
(void *)c
);
++a;
++b;
++c;
printf("%p\n%p\n%p\n\n",
(void *)a,
(void *)b,
(void *)c
);
return 0;
}
This outputs:
0x7ffed0c20690
0x7ffed0c20690
0x7ffed0c20690
0x7ffed0c20694
0x7ffed0c206a4
0x7ffed0c206a4
To me it looks like lines:
int (*b)[5] = &x;
int (*c)[5] = x;
achieve exact same result, because:
they assign the same addres (in case of *b it is the address of entire array and in case of *c it is the address of the first array member but those two overlap) and
assign same pointer size of 5 · int for *b and *c which leads to the exactly same pointer arithmetics when I increment the values.
Q1: Are there any hidden differences between definitions of *b and *c that I am missing?
Q2: Does pointer arithmetics only depends on the size of the pointer?
After you pointed I noticed that I do get an error:
main.c:9:16: warning: initialization of ‘int (*)[5]’ from incompatible pointer type ‘int *’ [-Wincompatible-pointer-types]
int (*c)[5] = x; // points to the 1st array element
Q1: Are there any hidden differences between definitions of *b and *c that I am
missing?
Pointer arithmetic on these two pointers will remain the same. Because internally array decays into the pointer to the first element in it, i.e. arr[n] will be converted to an expression of type "pointer to arr", and its value will be the address of the first element in the array.
Q2: Does pointer arithmetics only depends on the size of the pointer?
No, it depends on the size of the underlying type pointed to. Even in your provided sample input ++a and ++b are yielding different results. Because ++a offsets pointer by sizeof(int) which is 4. But in case of ++b your pointer is incremented by size of 5 * sizeof(int) by 20 (decimal)

Passing a two-dimensional array to a function in C confusion

I know there are many similar questions, but I can't find an answer.
When passing the two-dimensional [3] [4] array to the function in my code below, how does the compiler know how far to increment the pointer, in the case of the last printf() where we are incrementing 3 x 4 memory locations, if the number 3 is missing in the function argument?
I mean, why is only arr [] [4] sufficient and not [3] [4]? Thanks
#include <stdio.h>
#include <stdlib.h>
int Fun(int arr[][4])
{
printf("%p\n", arr); // address of first element
printf("%p\n", arr[0] + 1); // address increments by 4, pointing to next "inner array"
printf("%p\n", arr + 1); // how does it know to increment address by 3 x 4 here? The complete array size
}
int main()
{
int arr[3][4] =
{
1,2,3,4,
5,6,7,8,
9,10,11,12
};
printf("%p\n", arr);
printf("%p\n", arr[0] + 1);
printf("%p\n", arr + 1);
printf("Passing to function\n");
Fun(arr);
return 0;
}
First, Fun should be defined with:
int Fun(int arr[][4])
rather than what you have, int Fun(int* arr[][4]);.
Next, when Fun(arr) is evaluated, arr is automatically converted from an array of 3 arrays of 4 int to a pointer to an array of 4 int. Similarly, in the declaration of Fun, int arr[][4] is automatically adjusted to be a pointer to an array of 4 int. So the argument type and the parameter type will match if you declare Fun correctly.
You could also declare Fun as:
int Fun(int (*arr)[4])
This is the same thing as above, due to the automatic adjustment that would be applied to the declaration above. Note that the asterisk here is grouped with the arr by the parentheses. This makes it a pointer to an array of int, rather than an array of pointers to int.
Now, as to what will be printed, in main:
printf("%p\n", arr);
In this statement, arr will be automatically converted to a pointer to its first element, so it becomes a pointer to an array of 4 int. Then the value of this pointer is printed. Note: When printing pointers, technically you should convert them to const void * or void *, as with printf("%p\n", (const void *) arr);. However, omitting this likely does not cause a problem at the moment.
printf("%p\n", arr[0] + 1);
In this statement, arr[0] is the first element of arr. That first element is an array of 4 int, and it is automatically converted to be a pointer to its first element. So arr[0] becomes a pointer to the first int. Then adding 1 advances the pointer to the next int. The result is likely an address four bytes beyond arr, depending on your C implementation. (It could be a different number of bytes, but four is the most common today.)
printf("%p\n", arr + 1);
In this statement, arr is converted to a pointer to its first element, an array of 4 int. Adding 1 advances to pointer to the next element, which is the next array of 4 int. So this likely adds 16 bytes to the address.
Then, in Fun:
printf("%p\n", arr); // address of first element
Here arr is a pointer to an array of 4 int. Its value is printed, yielding the same address as for the corresponding printf in main.
printf("%p\n", arr[0] + 1); // address increments by 4, pointing to next "inner array"
Here arr[0] is the object pointed to by arr, which is an array of 4 int. Since it is an array, it is automatically converted to a pointer to its first element, which is an int. So this points to the first int. Then adding 1 advances to the next int, and this again yields the same address as the corresponding printf in main.
printf("%p\n", arr + 1); // how does it know to increment address by 3 x 4 here? The complete array size
In this case, arr is a pointer to an array of 4 int, and adding 1 advances it to the next array of 4 int, so the result is likely 16 bytes beyond the value of arr, and this again yields the same address as the corresponding printf in main.
If you saw different values for the printf statements in Fun and main, this was likely because of the incorrect declaration with int* and because int * is eight bytes in your C implementation, compared to four for int. That error would have doubled some of the increments. You should not have seen any multiple of three in the increments.
Regarding the first dimension, Fun does not need to know the first dimension because it never advances any pointers by units of the first dimension. It receives only a pointer to an array of 4 int, and it does not need to know that there are 3 such arrays there.
The detailed answer by Eric Postpischil clearly shows all the issues in OP's code.
I'd like to note that passing a pointer to the correct type would let the compiler doing the right pointer arithmetic:
#include <stdio.h>
#include <stdlib.h>
void Fun(int (*arr)[3][4])
{
printf("Address of the first element: %p\n", (void *)*arr);
printf("Address of the second row: %p\n", (void *)(*arr + 1));
printf("Address after the last element: %p\n", (void *)(arr + 1));
}
void Fun_vla(size_t rows, size_t cols, int (*arr)[rows][cols])
{
printf("Address of the first element: %p\n", (void *)*arr);
printf("Address of the second row: %p\n", (void *)(*arr + 1));
printf("Address after the last element: %p\n", (void *)(arr + 1));
}
int main()
{
int arr[3][4] =
{
{1,2,3,4},
{5,6,7,8},
{9,10,11,12}
};
Fun(&arr);
puts("");
Fun_vla(3, 4, &arr);
return 0;
}

why it's impossible to dereference a particular array index with the pointer notation

I have two different code examples. In the first code it is possible to dereference:
void getValue(int *myPointer)
{
*myPointer = 10000;
return;
}
int main()
{
int get_the_value = 2;
getValue(&get_the_value);
printf("The value of get_the_value = %d\n", get_the_value);
return 0;
}
But in the code below it is not possible to dereference *B in func().
I have this code below from the internet and they said this:
"B is a pointer to int, thus B[0] is an int, and you can't dereference an int. "
But is *myPointer in the first code not an int type too?
So my question is: Why does dereferencing work in the first code but not in the second code?
#include <stdio.h>
int func(int *B){
*B[0] = 5;
}
int main(void){
int B[3] = {1, 2, 3};
printf("b[0] = %d\n\n", B[0]);
func(&B);
printf("b[0] = %d\n\n", B[0]);
return 0;
}
B[0] is the same as *(B + 0), which is the same as *B, all of which have type int. So given the type of B, you can write either B[0] or *B.
Edit
You have a type mismatch which isn't helping things.
You've declared the parameter B in func to have type int *; like I said above, the expression B[0] is equivalent to *(B + 0), which is equivalent to *B. IOW, the subscript operator implicitly dereferences B.
In main, you've declared B as a 3-element array of int, so the type of the expression B is int [3]. Except when it is the operand of the sizeof or unary & operators, or is a string literal used to initialize a character array in a declaration, an expression of type T [N] will be converted ("decay") to an expression of type T *, and the value of the expression will be the address of the first element of the array.
However, when you call func, you pass the expression &B, which has type int (*)[3] (pointer to 3-element array of int). int * and int (*)[3] aren't compatible types. Now, it just so happens that both the expressions B and &B will evaluate to the same location (address of the first element of B), so the code will still "work" if you write either *B or B[0]. However, you should change the call to func to be
func( B ); // no & operator.
Then the types will match up.

Resources