The malloc()
function allocates at least the amount of memory you request though it may be more. However the amount of memory that malloc()
provides is not really the central issue here.
The C programming language is designed for speed and efficiency which means that many of the checks that other languages do are not done. So you can write a program that does something wrong and it will still work under some circumstances and in other circumstances not work.
In C a pointer is the address of a memory location. C does not check if the address is a valid address or not. C does not check that the amount of memory you are trying to use is the correct amount of memory.
So here is an annotated version of your program.
#include<conio.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void main() {
char *description; // create a variable that will contain the address of a character
clrscr();
// allocate an array of characters. the number of characters is 2 so this
// will allocate a minimum size memory area that will hold 2 characters.
// malloc() may round up the size or it may not but all you can count on
// is 2 characters in size.
description = malloc(2*sizeof(char));
// copy a constant string to the memory location pointed to by the variable
// description. the strcpy() function does not check on the limits of the
// memory. after the malloc() is done there is no size information available
// to strcpy() or any other of the C runtime library functions in the C Standard Library
// this is copying 11 characters plus the end of string for a total of 12 characters
// so to be correct the memory area pointed to by description should be at least
// 12 characters however we know from the malloc() above it is guaranteed to
// be only 2 characters so we are going past the memory area with this strcpy().
strcpy(description,"Hello there!");
// however we get lucky and it works anyway.
printf("%s",description);
// tell the memory allocation library that you are done with the memory and it
// can be used for something else now. the pointer will probably still be good
// for a while because the memory allocation, which gets its memory from the
// operating system, does not normally give any freed memory back to the OS.
// instead it normally just keeps it until the application terminates.
// as long as this freed memory is not used for something else, more than
// likely whatever you put there will remain there as is. however the moment
// the memory is given to something else, the values will change.
free(description);
printf("%s",description);
getch();
}
You can get an idea as to what is happening if you try the following sample program which is a modification of yours that instead of using malloc()
uses variables on the stack.
#include<conio.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void main() {
char array1[12] = { 0 };
char array2[2] = { 0 };
char array3[12] = { 0 };
char *description;
printf("Before\n array1 %s\n", array1);
printf(" array2 %s\n", array2);
printf(" array3 %s\n", array3);
description = &array2[0];
strcpy(description, "Hello there!");
printf("description %s\n", description);
printf("\nAfter\n array1 %s\n", array1);
printf(" array2 %s\n", array2);
printf(" array3 %s\n", array3);
}
Using Visual Studio 2013 and running in Debug mode I get a warning about memory being overwritten as the application terminates. When I do a release build and run it, there are no errors and I get the following output. As you can see the strcpy()
function just copied the characters overwriting adjacent memory. It looks like the Visual Studio 2013 compiler is aligning memory on a double word boundary so that only the last few characters of the string are visible in the adjacent memory area. Visual Studio padded the variable array2[]
so that the next variable allocated is on a double word boundary.
Before
array1
array2
array3
description Hello there!
After
array1
array2 Hello there!
array3 ere!
If we modify the above program to the following:
#include<conio.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void main() {
char array1[12] = { 0 };
char array2[2] = { 0 };
char array3[12] = { 0 };
char *description;
int *values;
printf("Before\n array1 %s\n", array1);
printf(" array2 %s\n", array2);
printf(" array3 %s\n", array3);
description = &array2[0];
strcpy(description, "Hello there!");
printf("description %s\n", description);
printf("\nAfter\n array1 %s\n", array1);
printf(" array2 %s\n", array2);
printf(" array3 %s\n", array3);
description = malloc(8 * sizeof(char));
strcpy(description, "this");
printf("\n\nMalloc\n first description %p %s\n", description, description);
free(description);
values = malloc(1 * sizeof(int));
*values = 0;
printf(" pointer %p and value %d\n", values, *values);
printf(" second description %p %s\n", description, description);
}
Then we get the following output. In this case we got lucky and were given the same memory area on the malloc()
of the int
so when we modified the int
we also modified the area pointed to by description
because once it was freed then malloc()
reused the area for the next allocation.
Before
array1
array2
array3
description Hello there!
After
array1
array2 Hello there!
array3 ere!
Malloc
first description 00944B28 this
pointer 00944B28 and value 0
second description 00944B28
The Resource Ownership Principle
This example demonstrates two rules when using malloc()
and free()
.
Allocate with malloc()
the amount you need and never exceed the amount of memory you requested. If you need more then look at the realloc()
function.
Once you free a memory area using free()
never use that pointer value again. You no longer own the memory area once it is freed.
When you use malloc()
you become the owner of the memory but only that memory that you requested. When you use free()
you relinquish ownership of the memory and you should not use it any more because you no longer own it.