You invoke Undefined Behavior by attempting to print double*
as double
on each iteration with your call printf("%f\n",a++);
and then again by attempting to access values beyond the end of your allocated block of memory on your call to printf (" %f\n", a[i]);
beginning when i = 3
and for each subsequent iteration of your for
loop.
Why are weird things happening?
When you declare double *a;
, a
is a pointer to double, not a double
itself. What is a pointer? A pointer is simply a variable that holds the address of something else as its value. When you attempt to call printf("%f\n",a++);
you are attempting to print the pointer, not the value at the address held by the pointer (which you obtain by dereferencing the pointer with the unary '*'
operator, e.g. *a
).
This is undefined behavior as described in C11 §7.21.6.1(p2 & p9) The fprintf function - insufficient arguments, or incorrect type.
Next, your attempt to both loop over each element with a for
loop while incrementing the pointer within the loop causes you to attempt to read values beyond the end of your allocated block beginning with i = 3
.
When you allocate memory for a
with a = calloc (n, sizeof(double));
(note: See: Do I cast the result of malloc?), you allocate n
times sizeof(double)
bytes initialized to all zero by using calloc
. (80-bytes on most modern desktops). You allocate space for 10 doubles
that can be access through indexes:
+---+---+---+---+---+- / -+---+---+
| 0 | 1 | 2 | 3 | 4 | ..\ ..| 8 | 9 |
+---+---+---+---+---+- / -+---+---+
One of the promises you make to the compiler when you allocate memory, is a promise you will only attempt to write or access values within the block, (you can reference the byte past the last value, but cannot attempt to read or write at that or any other address outside of your allocated block.
When you begin your loop, a
holds the address to the beginning of the block of allocated memory, e.g.
+---+---+---+---+---+- / -+---+---+
| 0 | 1 | 2 | 3 | 4 | ..\ ..| 8 | 9 |
+---+---+---+---+---+- / -+---+---+
^
a
On your first call to printf("%f\n",*a++);
, a side-effect of the post-increment operator (e.g. a++
) is that the pointer a
is advance by sizeof *a
bytes (or one double
). That is the entire basis of pointer arithmetic. So after your call to printf("%f\n",*a++);
, a
points to the second element, e.g.
+---+---+---+---+---+- / -+---+---+
| 0 | 1 | 2 | 3 | 4 | ..\ ..| 8 | 9 |
+---+---+---+---+---+- / -+---+---+
^
a
Your next call to printf("%f\n",a++);
invokes Undefined Behavior and then again advances the pointer by use of the post increment operator leaving a
pointing to the third-element, e.g.
+---+---+---+---+---+- / -+---+---+
| 0 | 1 | 2 | 3 | 4 | ..\ ..| 8 | 9 |
+---+---+---+---+---+- / -+---+---+
^
a
You then call printf("%f\n",a[i]);
which prints the third-element, and you for
loop increments i = 1
, and then cycle repeats, except with each increment, when you call printf("%f\n",a[i]);
, you reference the current value of a
plus the index. Meaning a[i]
is just array-index notation that is the equivalent of *(a + i)
in pointer notation.
Each iteration within your for
loop, you advance where a
points by 16-bytes (or by two double
values) by your use of a++
within the loop. On your 4th iteration (where i = 3
), immediately prior to your call to printf("%f\n",a[i]);
, a
points to one-before the last element of your allocated block, e.g.
+---+---+---+---+---+- / -+---+---+---+---+---+---+---+---+
| 0 | 1 | 2 | 3 | 4 | ..\ ..| 8 | 9 | you don't own this |
+---+---+---+---+---+- / -+---+---+---+---+---+---+---+---+
^ ^
a a[3]
So when you attempt to print with a[3]
(e.g. *(a + 3)
) you are attempting to print a value from an address 8-bytes (or one double
) past the end of your allocated block. By the end of the next iteration a
no longer points anywhere within the your allocated block of memory, e.g.
+---+---+---+---+---+- / -+---+---+---+---+---+---+---+---+
| 0 | 1 | 2 | 3 | 4 | ..\ ..| 8 | 9 | you don't own this |
+---+---+---+---+---+- / -+---+---+---+---+---+---+---+---+
^
a
Further, you have lost the ability to free(a)
, because you have failed to preserve a pointer to the beginning of the block of memory you have allocated. If you allocate a = calloc (n, sizeof(double));
, never increment a
itself without first saving a copy of the beginning address first. Generally you won't iterate a
, but instead a second pointer, such as double *p = a;
, then you can p++
all you like and still have the ability to free(a);
because a
still points to the beginning of the allocated block of memory. In your case, attempting to free(a)
will likely cause an immediate crash.
When you invoke Undefined Behavior, the behavior of your code can do anything from appear to act correctly to SegFault'ing. (what it will do is undefined). It is ill-formed code. So don't break your promise with the compiler, don't attempt to read or write to address outside the allocated block of memory.
Lastly, the proper declarations for main
are int main (void)
and int main (int argc, char **argv)
(which you will see written with the equivalent char *argv[]
). note: main
is a function of type int
and it returns a value. See: C11 Standard §5.1.2.2.1 Program startup p1 (draft n1570). See also: See What should main() return in C and C++?. void main
is an incorrect anachronism in virtually all systems, and all standard compliant systems.
Look things over and let me know if you have further questions.