Difference between pointer to pointer and pointer to array? - c

Given that the name of an array is actually a pointer to the first element of an array, the following code:
#include <stdio.h>
int main(void)
{
int a[3] = {0, 1, 2};
int *p;
p = a;
printf("%d\n", p[1]);
return 0;
}
prints 1, as expected.
Now, given that I can create a pointer that points to a pointer, I wrote the following:
#include <stdio.h>
int main(void)
{
int *p0;
int **p1;
int (*p2)[3];
int a[3] = {0, 1, 2};
p0 = a;
p1 = &a;
p2 = &a;
printf("p0[1] = %d\n(*p1)[1] = %d\n(*p2)[1] = %d\n",
p0[1], (*p1)[1], (*p2)[1]);
return 0;
}
I expected it to compile and print
p0[1] = 1
(*p1)[1] = 1
(*p2)[1] = 1
But instead, it goes wrong at compile time, saying:
test.c: In function ‘main’:
test.c:11:5: warning: assignment from incompatible pointer type [enabled by default]
Why is that assignment wrong? If p1 is a pointer to a pointer to an int and a is a pointer to an int (because it's the name of an array of ints), why can't I assign &a to p1?

Line 11 is
p1 = &a;
where p1 has type int ** and a has type int[3], right?
Well; &a has type int(*)[3] and that type is not compatible with int** as the compiler told you
You may want to try
p1 = &p0;
And read the c-faq, particularly section 6.
In short: arrays are not pointers, and pointers are not arrays.

a is not a pointer to int, it decays to such in certain situations. If &a was of type int ** you couldn't very well use it to initialize p2, could you?
You need to do p1 = &p0; for the effect you want. "pointer to pointer" means "at this address, you will find a pointer". But if you look at the address &a, you find an array (obviously), so int ** is not the correct type.

For many operations, a implies &a and both return the same thing: The address of the first item in the array.
You cannot get the address of the pointer because the variable does not store the pointer. a is not a pointer, even though it behaves like one in some cases.

Related

Pointers problems with C

I don't understand the difference in the t and p pointers. The t pointer gives the same output when printing t and *t only when using **t I get the value.
What's the difference between them?
The code is:
int main()
{
int B [2][3] = {
{2, 3, 6},
{4, 5, 8}
};
int *p = B;
int (*t)[3] = B;
printf ("%d\n", p);
printf ("%d\n", t);
printf ("%d\n", *p);
printf ("%d\n", *t);
printf ("%d\n", **t);
return 0;
}
Output is:
6422000
6422000
2
6422000
2
Comments have addressed the importance of using the correct format specifiers, but here are a couple of other points to consider:
point 1:
The declaration: int *p = B; should generate a compile time warning. This is because int *p is a simple pointer to int, as such it should only be set to point to the address of (&) a single int. But B does not represent an int.
For illustration, it is instructive to see the variations of warnings for the following 3 incorrect ways of initializing p with B. Each starts off with the phrase:
_"warning: incompatible pointer types initializing `int *` with an..."_:
int *p = B;//...expression of type 'int [2][3]'
int *p = &B;//...expression of type 'int (*)[2][3]'
int *p = &B[0]//...expression of type 'int (*)[3]'
Note the incompatible pointer types at the end of each warning. Each of them is specific, providing the programmer hints how to address the potential problem.
The following initializes *p with the address of a single int and generates no warning
int *p = &B[0][0];//assigning address of p to the location of a single integer value.
point 2:
The following declaration/assignment:
int (*t)[3] = B;
creates t as a pointer to an array of 3 int, and points t to the first instance (row) of 3 int in B where B is defined as:
int B [2][3] = {{2, 3, 6}, {4, 5, 8}};
Because t is defined in this way it is flexible in the way it can be used in that it is pointable to any array of 3 int i.e. it does not matter how many rows B has, or to which row t is pointed. Example, given the following arrays:
int B[5][3] = {{1,2,3}, {4,5,6}, {7,8,9}, {10,11,12}, {13,14,15}};
int A[3] = {0}; //A simple array of 3 int all set to 0
The following declarations can be made:
int (*t)[3] = NULL; //points to nothing
t = B; //points to location of 1st element of 1st row in B
t = &B[1]; //points to location of 1st element of 2nd row in B
t = &B[4]; //points to location of 1st element of 5th row in B
t = &A; //points to location of 1st element of A
Writing int *p = B; isn't a good idea but anyway, it puts the address of the very first element of B, 2 into p. So, p outputs the address(6422000) and *p outputs 2. All good till here.
What is t? It's a pointer to an array, B. What will happen when you print it, you'll get the address to B, which is always also the address of it's very first element, which happens to be 6422000. So, what will happen when you dereference t? You'll get and array, B in this case, which will then decay into a pointer and give you the memory address. And the memory address of B is 6422000. And **t will dereference the dereferenced array. The deference array is B, which will decay into the pointer, 6422000 in this case and that will be dereferenced again, giving 2.
Basically:
p: Address of the very first element of B, 2.
*p: Dereferenced p, in this case 2.
t: Address to B. Address of an array is also the address of it's very first element, equivalent to p.
*t: Dereferences into B, B will decay into it's very first element's pointer, equivalent to p.
**t: Dereferenced *t, which is 2.
Note: I know, the first element of B is {2, 3, 6}, not 2. I refer to 2 as "the very first element". That's inaccurate but for the purpose of explanation, I was forced to use the terminology.

Pointer variable pointing to a one dimensional array or two dimensional array?

I have the following code for a one dimensional array:
#include <stdio.h>
int main()
{
static int b[4] = {11, 12, 13, 14};
int (*p)[4];
p = b;
printf("%d \n", *(p + 1));
return 0;
}
Even though I consider "b (the array name)" as a pointer pointing to a one dimensional array, I got a compiling error as
'=': cannot convert from 'int [4]' to 'int (*)[4]'
However, if I change b array into a two dimensional array "a (the array name)", everything works fine. Does this mean that, in the usage of "int (*p)[4];", "*p" has to represent a[] as in the following:
static int a[3][4] = { {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12} };
int (*p)[4];
p = a;
As a result, "int (*p)[4]" only provides the flexibility on the number of rows of a two dimensional array.
Any insights on this problem?
Arrays naturally decay to pointers to their first elements, depending on context. That is, when such a decay happen then plain b is the same as &b[0], which have the type int *. Since the types of p and b (or &b[0]) are different you get an error.
As for a it's the same thing here, it decays to a pointer to its first element, i.e. a is the same as &a[0]. But since a[0] is an array of 4 elements, then &a[0] is a pointer to an array of four elements, or int (*)[4]. Which is also the type of p in the second example.
If you have an object of some type T like
T a;
then declaration of a pointer to the object will look like
T *p = &a;
Your array b has the type int[4]. So a pointer to the array will look like
int ( *p )[4] = &b;
To output the second element of the array using the pointer you should write
printf("%d \n", *( *p + 1 ) );
Thus your compiler issued the error message
cannot convert from 'int [4]' to 'int (*)[4]
because instead of writing at least
int ( *p )[4] = &b;
you wrote
int ( *p )[4] = b;
On the other hand, an array designator used in expressions with rare exceptions is implicitly converted to pointer to its first element. For example in this declaration
int *p = b;
the array b used as an initializer is converted to pointer to its firs element. The above declaration is equivalent to
int *p = &b[0];
or that is the same
int *p = b + 0;
Using this pointer you can call the function printf like
printf("%d \n", *(p + 1));
If you have a two-dimensional array as
int a[3][4];
then used in expressions it is converted to pointer to its first element that has the type int[4]. So you may write
int ( *p )[4] = a;
If you want to declare a pointer to the whole array as a single object you can write
int ( *p )[3][4] = &a;
a pointer pointing to a one dimensional array,
No, it points directly to the first element. Likewise:
int *p = b;
is enough.
The number 4 is not really part of any type here;
static int b[] = {11, 12, 13, 14};
It can be left out in the declaration. (Because it is the first dimension unless you make it 2D)
This (from AA)
int (*p)[4] = &b;
...
printf("%d \n", *( *p + 1 ) );
is just a obfuscated and overtyped version of:
int (*p)[] = &b;
...
printf("%d \n", (*p)[1] );
This replaces b with (*p), normally not what you want.

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)

Pointer to an array giving incompatible pointer type warning

I'm getting warning: assignment from incompatible pointer type [enabled by default] when i compile the following code:
int main() {
int (*aptr) [5] = NULL;
int arr[5] = {1,2,3,4,5};
aptr = &arr[0];
printf("aptr = %p\n arr = %p\n", aptr, &arr[0]);
return 0;
}
I'm getting the correct output:
aptr = 0xbfcc2c64
arr = 0xbfcc2c64
But why am I getting the warning of incompatible pointer type?
You declared a pointer to the entire array. Why are you trying to make it point to the first element?
If you want to declare your aptr with int (*)[5] type, as in your example, and make it point to arr, then this is how you are supposed to set the pointer value
aptr = &arr;
What you have in your code now is an attempt to assign a int * value to a pointer of int (*)[5] type. These are different types, which is why you get the warning (which is a constraint violation, AKA error, actually).
Array name itself give the base address it is not needed to use &arr.Moreover arr[0] represents the value at the first index.

C passing a pointer to an array on the stack

I am getting confused as to whether it is valid (in C) to pass a pointer to an array that has been initiated as follows (e.g. at compile time on the stack):
int a[] = {1, 2, 3};
my_func(&a);
void my_func(int **a)
{
int *tmp = *a; /* this statement fixes the problem */
printf("%d %d %d", (*a)[0], (*a)[1], (*a)[2]); /*doesn't work */
printf("%d %d %d", tmp[0], tmp[1], tmp[2]); /*does work */
}
when I step through this with gdb I can't 'see' any of the values (*a)[0], etc. from 'inside' my_func. e.g.
(gdb) p (*a)[0]
Cannot access memory at address 0x0
I'm thinking that possibly I have a fundamental misunderstanding with regard to what you can and can't do with arrays that are on the stack rather than the heap?
I hope thats not the case as it is very convenient for my unit tests to declare arrays on the stack as in the example, but I need to test functions that are expecting pointers to pointers to int.
Note I do get a compiler warning as follows:
test_utility.c:499:5: warning: passing argument 1 of ‘int_array_unique’ from incompatible pointer type [enabled by default]
../src/glamdring2.h:152:5: note: expected ‘int **’ but argument is of type ‘int (*)[12]’
but I thought it would be ok to mix int *a[] with **a? Perhaps not? Are they not equivalent?
a[] is an array, not a pointer ("not an lvalue"); in your function call
func( &a);
&a decays to a pointer to int; &a is not a pointer to pointer to int. Why? there is no pointer to point to.
The function prototype
void func( int **p);
expects a pointer to pointer to int, that does not fit the function being called with a pointer to int as an argument, like you did.
UPDATE: I don't know what the OP's intentions were, so this is just a guess...
void my_func(int *a);
int a[] = {1, 2, 3};
my_func(a); /* note: this is equivalent to my_func( &a ); */
void my_func(int *a)
{
printf("%d %d %d\n", a[0], a[1], a[2] );
}
printf("%p vs %p vs %p\n",&a,&a[0],a);
&a &a[0] and a - are all the same - the address of the first int in the array
void my_func(int **a);
int main(int ac, char *av[]) {
int a[] = {1, 2, 3};
int *p = a;
printf("%p vs %p vs %p\n",&a,&a[0],a); //are all the same - the address of the first int in the array
my_func(&p);
return 0;
}
void my_func(int **a) {
printf("%d %d %d", (*a)[0], (*a)[1], (*a)[2]);
}
http://www.ibiblio.org/pub/languages/fortran/append-c.html
http://publications.gbdirect.co.uk/c_book/chapter5/arrays_and_address_of.html
It's perfectly fine to declare an array on the stack and pass its address to a function, so long as the function doesn't try to store a reference to the array for later use.
Not sure why you're adding the extra indirection to the the argument, though. Why not just declare my_func to take an int* as a parameter, and simply pass a as the argument? This is less likely to confuse the debugger.
You are assuming that sizeof(int) == sizeof(void *). This might not be true. I am not sure if this is the cause of your problem, but at least I would start by either adding a runtime test asserting the assumption, or probably better change from int to pointer.

Resources