Please can someone explain to me this strange pointer behaviour. Did I miss something?!?
start first attempt
int *foo=(int*)malloc(sizeof(int)*4);//allocates memory for 4 integer with size of sizeof(int) foo points to the address of the first element- foo=&foo[0]
for (int i = 0; i < 4; i++)
{
*foo=i+1;
foo++;
printf("foo[%d] :%d n",i,foo[i]);
}
Otput is something like this
foo[0] :0
foo[1] :0
foo[2] :0
foo[3] :0
end first attempt
Lets add one tiny thing to our code:
start second attempt
int *foo=(int*)malloc(sizeof(int)*4);//same as first attempt
int* ptr=foo;//???????????????????? why this is necessary?
printf("Value of foo: %d value of ptr is: %d &foo[0] %dn",foo,ptr, &foo[0]);
for (int i = 0; i < 4; i++)
{
*ptr=i+1;//assign and update via ptr
ptr++;
printf("foo[%d] :%d n",i,foo[i]);
}
Output
Value of foo: 1322628816 value of ptr is: 1322628816 &foo[0] 1322628816
foo[0] :1
foo[1] :2
foo[2] :3
foo[3] :4
end second attempt
Note: I use Ubuntu 18.04, gcc (Ubuntu 11.4.0-2ubuntu1~18.04) 11.4.0 with GNU C Library (Ubuntu GLIBC 2.27-3ubuntu1.6) stable release version 2.27
I try exactly what I wrote above
3
Answers
After you do
foo++
,foo
no longer points to the beginning of the array, it points thei+1
th element of the array. When you then try to accessfoo[i]
, you’re accessing the elementi
places after this element, i.e.original_foo[i+1+i]
.It works in the second version because the pointer you’re indexing is not the one you’re incrementing. So
foo
continues to point to the beginning of the array, andfoo[i]
is the element you just assigned.This initializes the first
int
of the four-int
area youmalloc()
ed.This makes
foo
point to the secondint
.You’re printing
foo + i
, i.e. the second (uninitialized)int
, invoking UB.Things get only worse from there, since you keep incrementing
foo
andi
… in the end, you’re reading from memory you haven’t evenmalloc()
ed.Your second example works since you increment
ptr
for writing, andi
(as index onfoo
) for reading.i+1
to theint
foo
points at. That in itself is not wrong.foo
point at the next element in the allocated memory. You’ve now lost the base pointer – unless you later restore it by counting backwards or saving the pointer before the loop starts.foo[i]
you add yeti
to where the base pointer points. You’ve now effectively made it*(original_foo + i + i)
which very soon becomes out of bounds.An idiomatic solution would be to not change
foo
at all:Demo
Regarding:
That’s the saving of the base pointer I mentioned as a possible workaround in point 2 above. In the following loop, you only change
ptr
and let the base pointerfoo
be untouched. Dereferencingfoo[i]
(same as*(foo + i)
) then does the correct thing.i+1
to whereptr
points.int*
one step ahead.foo[i]
(*(foo + i)
) which dereferences the same address thatptr
pointed at prior toptr++
.