What was taught is that malloc(10*sizeof(char))
allocates enough
bytes on the heap to store 10 characters and returns a pointer to the
first byte which can be saved in another variable as follows char *x
= malloc(10*sizeof(char))
. To free the memory one would use free(x)
.
"On the heap" is an implementation concept, not a C-language concept. The C language itself is not concerned with partitioning memory into separate areas with different characteristics, and in fact it is not necessarily the case that any given C implementation in fact does so.
Even in an introductory course -- maybe especially in an introductory course -- it is better to use C language concepts than concepts native to a certain implementation style. The relevant C language concept in this case is storage duration:
An object has a storage duration that determines its lifetime. There are four storage durations: static, thread, automatic, and allocated.
(C2011, 6.2.4/1)
The object allocated by your malloc()
call, (in)to which your pointer x
points, has "allocated" duration. That means its lifetime lasts until that object is freed by a call to free()
. Note well the distinction here between variable x
, a pointer with automatic storage duration, and the object to which x
initially points, an untyped object the size of 10 char
s.
There is (much) more, but this point in your journey is early for delving deeply into the standard. Nevertheless, I find this characterization more useful for addressing questions such as those you pose.
But there is another way to make a computer to reserve enough memory
to store 10 characters i.e. char c[10]
.
Yes, that's true.
- Is in the code snippet above c also a pointer of type
char*
?
No. Within the scope of that declaration, the identifier c
refers to an array of 10 char
s. There is a close relationship between arrays and pointers, but they are not at all the same thing. This is a crucially important point, and one over which many new C programmers stumble, so I repeat: arrays and pointers are not the same thing. The details would make for a whole other answer, however, and one which you can already find several times over here on SO.
To put it another way, identifier c
designates one kind of thing to which x
's value could point, but remember that x
's (pointer) value is distinct from the object to which it points.
- Does
char c[10]
also reserve memory on the heap as malloc
does?
If your declaration of c
appears inside a function then it declares an array with automatic storage duration. This means that the array's lifetime lasts until identifier c
goes out of scope. It is the implementation's concern where the storage for that array is located, but on an implementation that provides a heap / stack distinction, the storage would most likely be on the stack, not the heap.
- Are the ways to allocate memory equivalent?
No. malloc()
allocates an object with allocated storage duration, whose lifetime the program is responsible for managing explicitly. The other allocates an object with automatic storage duration, whose lifetime is determined by the identifier's scope.
char c[3] = "aaa";free(c);
returns a runtime error; so it seems I can not free the memory I have allocated with char c[3]
. Why
is that?
Most directly, it is because the specifications for the free()
function explicitly say
[I]f the argument does not match a pointer earlier returned by a memory
management function, or if the space has been deallocated by a call to
free or realloc, the behavior is undefined.
(C2011, 7.22.3.3/2)
That is, the standard does not require a runtime error (or any particular other behavior) if you try to free a pointer to an object with automatic duration, but it explicitly disclaims any promise that you can free memory that way.
But a more satisfying answer, I think, is that free()
is how you mark the end of the lifetime of an object with allocated storage duration, not one with automatic (or other) duration. Where the storage for the object is located (e.g. stack vs. heap) is ancillary.
int a = 5;
, a pointer would simply hold the address where5
was stored, e.g.int *b = &a;
. With any dynamic allocation, the only way you have of accessing the memory block is through a pointer to the starting address. (you must preserve a porter to the starting address to later be able tofree
the memory). In this light,malloc
returning a pointer to the first byte is as it must be. – Cook