@JohnathanLeffler nailed it. "The string literal is probably stored in the code [data] segment, where it will be non-writable." You can see this with a little more probing.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Do this in a function so I can verify what is stack and what is heap.
* Try to return a variable and see if the compiler warns that it's
* stack memory!
*/
static char *test_mem() {
char *stack = "whom";
char *heap = malloc(sizeof(char) * 5);
printf("char *stack = \"%s\" at %p (size %zu) inner %p (size %zu)\n",
stack, stack, sizeof(stack), &stack, sizeof(&stack));
printf("char *heap = malloc at %p (size %zu) inner %p (size %zu)\n",
heap, sizeof(heap), &heap, sizeof(&heap));
return heap;
}
int main(void)
{
char *ret = test_mem();
printf("%s\n", ret);
return 0;
}
char *stack = "whom" at 0x102fc0f28 (size 8) inner 0x7fff5cc3f4d8 (size 8)
char *heap = malloc at 0x7f9362404c80 (size 8) inner 0x7fff5cc3f4d0 (size 8)
Note that only the string literal is in the lower memory address. All the rest are in basically the same location. When I open up the executable in a hex editor I can find a block of strings.
00000f20: dcff ffff 2573 0a00 7768 6f6d 0063 6861 ....%s..whom.cha
00000f30: 7220 2a73 7461 636b 203d 2022 2573 2220 r *stack = "%s"
00000f40: 6174 2025 7020 2873 697a 6520 257a 7529 at %p (size %zu)
00000f50: 2069 6e6e 6572 2025 7020 2873 697a 6520 inner %p (size
00000f60: 257a 7529 0a00 6368 6172 202a 6865 6170 %zu)..char *heap
00000f70: 203d 206d 616c 6c6f 6320 6174 2025 7020 = malloc at %p
00000f80: 2873 697a 6520 257a 7529 2069 6e6e 6572 (size %zu) inner
00000f90: 2025 7020 2873 697a 6520 257a 7529 0a00 %p (size %zu)..
The string foo
is not stored in the heap, it is in the data segment of the executable which, as you guessed, is accessed as virtual memory which is allocated a special range.
Finally, the output shows those pointers are all of the same size. They're all (in my case) 64 bit (8 byte) pointers.