Let's divide this answer in two points of view of the same stuff, because the standards only complicate understanding of this topic, but they're standards anyway :).
Subject common to both parts
void func1() { char *s = "hello"; char *c; int b; c = (char*)malloc(15); strcpy(c, s); }
Part I: From a standardese point of view
According to the standards, there's this useful concept known as automatic variable duration, in which a variable's space is reserved automatically upon entering a given scope (with unitialized values, a.k.a: garbage!), it may be set/accessed or not during such a scope, and such a space is freed for future use. Note: In C++, this also involves construction and destruction of objects.
So, in your example, you have three automatic variables:
char *s, which gets initialized to whatever the address of "hello" happens to be. char *c, which holds garbage until it's initialized by a later assignment. int b, which holds garbage all of its lifetime.
BTW, how storage works with functions is unspecified by the standards.
Part II: From a real-world point of view
On any decent computer architecture you will find a data structure known as the stack. The stack's purpose is to hold space that can be used and recycled by automatic variables, as well as some space for some stuff needed for recursion/function calling, and can serve as a place to hold temporary values (for optimization purposes) if the compiler decides to.
The stack works in a PUSH/POP fashion, that is, the stack grows downwards. Let my explain it a little better. Imagine an empty stack like this:
[Top of the Stack] [Bottom of the Stack]
If you, for example, PUSH an int of value 5, you get:
[Top of the Stack] 5 [Bottom of the Stack]
Then, if you PUSH -2:
[Top of the Stack] 5 -2 [Bottom of the Stack]
And, if you POP, you retrieve -2, and the stack looks as before -2 was PUSHed.
The bottom of the stack is a barrier that can be moved uppon PUSHing and POPing. On most architectures, the bottom of the stack is recorded by a processor register known as the stack pointer. Think of it as a unsigned char*. You can decrease it, increase it, do pointer arithmetic on it, etcetera. Everything with the sole purpose to do black magic on the stack's contents.
Reserving (space for) automatic variables in the stack is done by decreasing it (remember, it grows downwards), and releasing them is done by increasing it. Basing us on this, the previous theoretical PUSH -2 is shorthand to something like this in pseudo-assembly:
SUB %SP, $4 # Subtract sizeof(int) from the stack pointer MOV $-2, (%SP) # Copy the value `-2` to the address pointed by the stack pointer
POP whereToPop is merely the inverse
MOV (%SP), whereToPop # Get the value ADD %SP, $4 # Free the space
Now, compiling func1() may yield the following pseudo-assembly (Note: you are not expected to understand this at its fullest):
.rodata # Read-only data goes here! .STR0 = "hello" # The string literal goes here .text # Code goes here! func1: SUB %SP, $12 # sizeof(char*) + sizeof(char*) + sizeof(int) LEA .STR0, (%SP) # Copy the address (LEA, load effective address) of `.STR0` (the string literal) into the first 4-byte space in the stack (a.k.a `char *s`) PUSH $15 # Pass argument to `malloc()` (note: arguments are pushed last to first) CALL malloc ADD %SP, 4 # The caller cleans up the stack/pops arguments MOV %RV, 4(%SP) # Move the return value of `malloc()` (%RV) to the second 4-byte variable allocated (`4(%SP)`, a.k.a `char *c`) PUSH (%SP) # Second argument to `strcpy()` PUSH 4(%SP) # First argument to `strcpy()` CALL strcpy RET # Return with no value
I hope this has led some light on you!
"hello"will be stored in DS . Pointers can be on stack or data depending on context . Butbwill be on stack .const char*is an ancient C bug that's impossible to fix because it will break most any existing program :) Just try changing it, dollar to donuts it will say bang!