Reputation: 1633
I'm very confused about what's happening. I always thought char *
and char []
were interchangable, but after looking at the memory addresses it seems char *
allocates space in the heap, whereas char []
is allocating memory on the stack.
char stack[] = "hello";
char *heap = "hello";
char *heap_string_malloc = malloc(5);
heap_string_malloc = "hello";
printf("Address of stack[0]: %p\n", stack);
printf("Address of heap[0]: %p\n", heap);
printf("Address of heap_string_malloc[0]: %p\n", heap_string_malloc);
Outputs the following:
Address of stack[0]: 0x7fff8b0b85b0
Address of heap[0]: 0x400760
Address of heap_string_malloc[0]: 0x400760
Does this mean that char *
is dynamically allocated?
Further to my confusion, how come malloc
is allocating the same memory address as what char *heap
has already allocated? I'm not running any optimisation (simply gcc file.c
).
Upvotes: 38
Views: 27237
Reputation: 123458
String literals such as "hello"
are stored in such a way that they are held over the lifetime of the program. They are often stored in a separate data segment (distinct from the stack or heap) which may be read-only.
When you write
char stack[] = "hello";
you are creating a new auto
("stack") variable of type "6-element array of char
" (size is taken from the length of the string literal), and the contents of the string literal "hello"
are copied to it.
When you write
char *heap = "hello";
you are creating a new auto
("stack") variable of type "pointer to char
", and the address of the string literal "hello"
is copied to it.
Here's how it looks on my system:
Item Address 00 01 02 03
---- ------- -- -- -- --
"hello" 0x400b70 68 65 6c 6c hell
0x400b74 6f 00 22 68 o."h
stack 0x7fffb00c7620 68 65 6c 6c hell
0x7fffb00c7624 6f 00 00 00 o...
heap 0x7fffb00c7618 70 0b 40 00 p.@.
0x7fffb00c761c 00 00 00 00 ....
*heap 0x400b70 68 65 6c 6c hell
0x400b74 6f 00 22 68 o."h
As you can see, the string literal "hello"
has its own storage, starting at address 0x400b70. Both the stack
ahd heap
variables are created as auto
("stack") variables. stack
contains a copy of the contents of the string literal, while heap
contains the address of the string literal.
Now, suppose I use malloc
to allocate the memory for the string and assign the result to heap
:
heap = malloc( sizeof *heap * strlen( "hello" + 1 ));
strcpy( heap, "hello" );
Now my memory map looks like the following:
Item Address 00 01 02 03
---- ------- -- -- -- --
"hello" 0x400b70 68 65 6c 6c hell
0x400b74 6f 00 22 68 o."h
stack 0x7fffb00c7620 68 65 6c 6c hell
0x7fffb00c7624 6f 00 00 00 o...
heap 0x7fffb00c7618 10 10 50 00 ..P.
0x7fffb00c761c 00 00 00 00 ....
*heap 0x501010 68 65 6c 6c hell
0x501014 6f 00 00 00 o...
The heap
variable now contains a different address, which points to yet another 6-byte chunk of memory containing the string "hello".
EDIT
For byteofthat, here's the code I use to generate the above map:
dumper.h:
#ifndef DUMPER_H
#define DUMPER_H
/**
* Dumps a memory map to the specified output stream
*
* Inputs:
*
* names - list of item names
* addrs - list of addresses to different items
* lengths - length of each item
* count - number of items being dumped
* stream - output destination
*
* Outputs: none
* Returns: none
*/
void dumper(char **names, void **addrs, size_t *lengths, size_t count, FILE *stream);
#endif
dumper.c:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include "dumper.h"
/**
* Dumps a memory map to the specified output stream
*
* Inputs:
*
* names - list of item names
* addrs - list of addresses to different items
* lengths - length of each item
* count - number of items being dumped
* stream - output destination
*
* Outputs: none
* Returns: none
*/
void dumper(char **names, void **addrs, size_t *lengths, size_t count, FILE *stream)
{
size_t i;
int maxlen = 15;
for ( size_t j = 0; j < count; j++ )
{
if (strlen(names[j]) > maxlen && strlen(names[j]) < 50)
maxlen = strlen(names[j]);
}
fprintf(stream,"%*s%15s%5s%5s%5s%5s\n", maxlen, "Item", "Address", "00", "01",
"02", "03");
fprintf(stream,"%*s%15s%5s%5s%5s%5s\n", maxlen, "----", "-------", "--", "--",
"--", "--");
for (i = 0; i < count; i++)
{
size_t j;
char *namefield = names[i];
unsigned char *p = (unsigned char *) addrs[i];
for (j = 0; j < lengths[i]; j+=4)
{
size_t k;
fprintf(stream,"%*.*s", maxlen, maxlen, namefield);
fprintf(stream,"%15p", (void *) p);
for (k = 0; k < 4; k++)
{
fprintf(stream,"%3s%02x", " ", p[k]);
}
fprintf(stream, " ");
for ( k = 0; k < 4; k++)
{
if (isgraph(p[k]))
fprintf(stream,"%c", p[k]);
else
fprintf(stream, ".");
}
fputc('\n', stream);
namefield = " ";
p += 4;
}
fputc('\n', stream);
}
}
And an example of how to use it:
#include <stdio.h>
#include "dumper.h"
int main(void)
{
int x = 0;
double y = 3.14159;
char foo[] = "This is a test";
void *addrs[] = {&x, &y, foo, "This is a test"};
char *names[] = {"x", "y", "foo", "\"This is a test\""};
size_t lengths[] = {sizeof x, sizeof y, sizeof foo, sizeof "This is a test"};
dumper(names, addrs, lengths, 4, stdout);
return 0;
}
Upvotes: 13
Reputation: 247969
This creates an array on the stack, containing a copy of the static string "hello":
char stack[] = "hello";
This creates a pointer on the stack, containing the address of the static string "hello":
char *heap = "hello";
This creates a pointer on the stack, containing the address of a dynamically allocated buffer of 5 bytes:
char *heap_string_malloc = malloc(5);
But in all three cases, you put something on the stack. char*
is not "on the heap". It is a pointer (on the stack) which points to something, somewhere.
Upvotes: 1
Reputation: 119
"stack" is a static array of chars so it will be allocated in the stack and freed automatically once the function ends, because its size is known since its definition. While "heap" and "heap_string_malloc" are both declared as pointers to char buffers and need to be allocated dynamicly with malloc in order to define the size of their content, that's why they will reside in the heap memory. By doing:
char *heap = "hello";
and:
heap_string_malloc = "hello";
you are modifying the pointers them-self (with a static buffer value), not the content to which they're pointing. You should rather use memcpy to modify the memory pointed by the "heap_string_malloc" pointer with your data:
memcpy(heap_string_malloc, "hello", 5);
Upvotes: 0
Reputation: 409176
When you do e.g.
char *heap = "hello";
the pointer heap
actually does not point to the heap, it points to static data loaded together with the rest of program by the operating system loader. In fact, to be correct it should be
const char *heap = "hello";
as heap
is pointing to a constant and read-only piece of memory.
Also, while arrays decays to (and can be used as) pointers, and pointers can be used with array syntax, they are not the same. The biggest difference being that for an array you can use e.g. sizeof
to get the size in bytes of the actual array, while it's not possible for pointers.
And as a third thing, when you're doing
char *heap_string_malloc = malloc(5);
heap_string_malloc = "hello";
you have a memory leak, as you first assign something to heap_string_malloc
but then directly afterward reassign heap_string_malloc
to point to something completely different.
As for the reason you get the same address for both heap
and heap_string_malloc
it's because both points to the same literal string.
Upvotes: 17
Reputation: 363567
Arrays are not pointers. What your program is doing, line by line, is
// Allocate 6 bytes in the stack and store "hello" in them
char stack[] = "hello";
// Allocate pointer on the stack and point it to a static, read-only buffer
// containing "hello"
char *heap = "hello";
// Malloc 5 bytes (which isn't enough to hold "hello" due to the NUL byte)
char *heap_string_malloc = malloc(5);
// Reset heap_string_malloc to point to a static buffer; memory leak!
heap_string_malloc = "hello";
The reason you're seeing the same pointer twice is because the compiler optimized away the second static buffer containing "hello"
.
Upvotes: 45