I am dumping raw memory contents to show you what happens when you declare a variable on the stack, and allocate memory with malloc.
For the purposes of this, you can ignore the contents of the 'printpage' and 'spew' functions, they're convenience functions I made to dump memory and are not related otherwise.
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
void printpage(void *pointer, FILE *fout) {
unsigned long p=(unsigned long int) pointer;
unsigned long size=~(unsigned long)(getpagesize()-1);
unsigned char *pp;
p &= size;
pp=(unsigned char *)p;
fwrite(pp, getpagesize(), 1, fout);
}
void spew(const char *msg, const void *ptr) {
FILE *fp=popen("hexdump -C", "w");
unsigned long size=(unsigned long)(getpagesize()-1);
printf("%7s @ %08lx(%08lx+%08lx)\n", msg,
(unsigned long)ptr,
((unsigned long)ptr)&(~size),
(unsigned long)ptr&size);
fflush(stdout);
printpage((void *)ptr, fp);
pclose(fp);
fflush(stdout);
printf("\n");
fflush(stdout);
}
int main(int argc, char *argv[])
{
char *mem1=malloc(16);
char *mem2=malloc(16);
char *mem3=malloc(16);
char *mem4=malloc(16);
mem1[0]='A'; mem2[0]='B'; mem3[0]='C'; mem4[0]='D';
printf("mem1=%p mem2=%p mem3=%p mem4=%p\n", mem1, mem2, mem3, mem4);
spew("heap", mem1);
spew("stack", &mem1);
// Now, what happens if I do mem1=mem3 ?
mem1=mem3;
spew("heap after", mem1);
spew("stack after",&mem1);
return(0);
}
Remember that memory can be considered to be one gigantic array of bytes, from address 00000000 all the way up to ffffffff (on a 32-bit machine). Pointers are just array indexes inside this array.
So we allocate four blocks of 16 bytes, set their first element to something, and dump memory to see where they ended up:
mem1=0x085ef008 mem2=0x085ef020 mem3=0x085ef038 mem4=0x085ef050
heap @ 085ef008(085ef000+00000008)
00000000 00 00 00 00 19 00 00 00 41 00 00 00 00 00 00 00 |........A.......|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 19 00 00 00 |................|
00000020 42 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |B...............|
00000030 00 00 00 00 19 00 00 00 43 00 00 00 00 00 00 00 |........C.......|
00000040 00 00 00 00 00 00 00 00 00 00 00 00 19 00 00 00 |................|
00000050 44 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |D...............|
00000060 00 00 00 00 b1 00 00 00 84 2c ad fb 00 00 8c b7 |.........,......|
00000070 00 00 8c b7 00 00 8c b7 00 00 8c b7 00 00 8c b7 |................|
00000080 00 10 8c b7 00 00 8c b7 00 10 8c b7 00 00 00 00 |................|
00000090 00 00 00 00 00 00 00 00 00 00 00 00 60 45 8b b7 |............`E..|
000000a0 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000000b0 08 f1 5e 08 ff ff ff ff ff ff ff ff 00 00 00 00 |..^.............|
000000c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000000d0 ff ff ff ff 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000000e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000000f0 00 00 00 00 00 00 00 00 00 00 00 00 00 36 8b b7 |.............6..|
00000100 5f 74 00 00 00 00 00 00 01 00 00 00 01 00 00 00 |_t..............|
00000110 c0 e6 76 b7 f1 0e 02 00 00 00 00 00 00 00 00 00 |..v.............|
00000120 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00001000
(continued)
...but what about mem1 itself? Something, somewhere, has to remember that address of 0x85ef008, yes? And it does, on the "stack", which is a big block of memory which the processor uses as temporary space.
There's actually quite a lot of stuff on the stack. It's not just us that's using it. Every time you call a function, it uses stack to pass arguments, create local variables, and remember where to return. It gets used so much, in fact, that you can't trust that a local variable doesn't contain previously-used garbage values unless you set it to anything else yourself.
stack @ bf8596b0(bf859000+000006b0)
00000000 81 cb 7a b7 c0 44 8b b7 84 88 04 08 02 00 00 00 |..z..D..........|
00000010 00 00 00 00 88 88 04 08 05 00 00 00 01 00 00 00 |................|
00000020 c0 ff ff ff c0 ff ff ff c0 ff ff ff 54 bb 7a b7 |............T.z.|
<lots of garbage snipped>
000006a0 50 f0 5e 08 f4 3f 8b b7 c8 96 85 bf c9 87 04 08 |P.^..?..........|
000006b0 08 f0 5e 08 20 f0 5e 08 38 f0 5e 08 50 f0 5e 08 |..^. .^.8.^.P.^.|
000006c0 b0 87 04 08 d0 84 04 08 28 97 85 bf b5 5b 78 b7 |........(....[x.|
000006d0 01 00 00 00 54 97 85 bf 5c 97 85 bf 98 2a 8e b7 |....T...\....*..|
000006e0 b0 26 8c b7 01 00 00 00 01 00 00 00 00 00 00 00 |.&..............|
000006f0 f4 3f 8b b7 b0 87 04 08 d0 84 04 08 28 97 85 bf |.?..........(...|
...
(continued)
It's there all right. Slightly out-of-order, but it's there. That's the order an x86 processor handles all numbers, nothing weird. Humans are weird in wanting it highest-to-lowest digit order instead of something easily mechanically processable, which is why we have printf to handle that job for us.
Now, we want to put a different string into mem1. What will the statement 'mem1=mem3' do?
heap after @ 085ef038(085ef000+00000038)
00000000 00 00 00 00 19 00 00 00 41 00 00 00 00 00 00 00 |........A.......|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 19 00 00 00 |................|
00000020 42 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |B...............|
00000030 00 00 00 00 19 00 00 00 43 00 00 00 00 00 00 00 |........C.......|
00000040 00 00 00 00 00 00 00 00 00 00 00 00 19 00 00 00 |................|
00000050 44 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |D...............|
00000060 00 00 00 00 b1 00 00 00 84 2c ad fb 00 00 8c b7 |.........,......|
00000070 00 00 8c b7 00 00 8c b7 00 00 8c b7 00 00 8c b7 |................|
00000080 00 10 8c b7 00 00 8c b7 00 10 8c b7 00 00 00 00 |................|
00000090 00 00 00 00 00 00 00 00 00 00 00 00 60 45 8b b7 |............`E..|
000000a0 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000000b0 08 f1 5e 08 ff ff ff ff ff ff ff ff 00 00 00 00 |..^.............|
000000c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000000d0 ff ff ff ff 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000000e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000000f0 00 00 00 00 00 00 00 00 00 00 00 00 00 36 8b b7 |.............6..|
00000100 61 74 00 00 00 00 00 00 01 00 00 00 01 00 00 00 |at..............|
00000110 c0 e6 76 b7 f1 0e 02 00 00 00 00 00 00 00 00 00 |..v.............|
00000120 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00001000
It looks like -- absolutely nothing. Except... What happened on the stack?
stack after @ bf8596b0(bf859000+000006b0)
00000000 81 cb 7a b7 c0 44 8b b7 84 88 04 08 02 00 00 00 |..z..D..........|
00000010 00 00 00 00 88 88 04 08 05 00 00 00 01 00 00 00 |................|
00000020 c0 ff ff ff c0 ff ff ff c0 ff ff ff 54 bb 7a b7 |............T.z.|
<lots of garbage snipped>
000006a0 50 f0 5e 08 f4 3f 8b b7 c8 96 85 bf c9 87 04 08 |P.^..?..........|
000006b0 38 f0 5e 08 20 f0 5e 08 38 f0 5e 08 50 f0 5e 08 |8.^. .^.8.^.P.^.|
000006c0 b0 87 04 08 d0 84 04 08 28 97 85 bf b5 5b 78 b7 |........(....[x.|
000006d0 01 00 00 00 54 97 85 bf 5c 97 85 bf 98 2a 8e b7 |....T...\....*..|
If mem1 is a pointer to a string, mem1=... does not modify the string. It alters the pointer.
Also: free() requires the exact same pointer that malloc() gave you. If you give it a different pointer, even slightly, it will crash.
If you give it the same pointer twice, it will crash. (Which means, if we did free() on all our pointers, our program would crash right now.)
If you write beyond the end of the memory you allocated, it will probably crash. (Because, as you can see in the dump, if you write beyond that you're stomping on top of something else).