Wow, vedo una massiccia confusione nei post di cuttyflam. Intanto perchè continui ad usare termini tipicamente del DOS, tipo "terminale del DOS" e "memoria estesa"?
Sono concetti che non dovrebbero far parte del libro che stai usando per studiare il C ( almeno spero ). E ti stanno portando fuori strada. Per esempio, quando dici che heap = memoria estesa, stai sbagliando di grosso.
Heap e stack sono due modi di gestire la memoria. E si, il sistema operativo si preoccupa d'individuare due aree di memoria distinte in cui "allocare" queste due tipologie di memoria ( termine improprio, ma lasciamolo passare ).
Il punto è che lo stack ( nella stragrande maggioranza dei linguaggi ) è gestito automaticamente dal runtime del linguaggio e non esplicitamente dal programmatore. Mentre l'heap si, nei linguaggi di sistema tipo C, C++, Rust, Pascal, D, ecc... In linguaggi come Python, nemmeno quello puoi gestire.
E qui si arriva a malloc/free/new/delete, che sono le funzioni e parole chiave per allocare/deallocare zone dell'heap, ottenendo puntatori all'inizio di tali zone.
La contiguità di cui parlavi, è presente in alcuni casi ma non sempre. Un array sta su un blocco contiguo di memoria ( che sia heap o stack, dipende da come l'hai dichiarato nel programma ). Una lista linkata non sta su un unico blocco contiguo di memoria, ma ha i suoi elementi sparpagliati in tutto l'heap.
Per tornare alla tua domanda iniziale, una variabile di tipo stringa ( char [] ) è un array di caratteri e tutte le variabili che fanno riferimento agli array, contengono in realtà l'indirizzo ( il puntatore ) della locazione di memoria dove inizia l'array. NON SONO PUNTATORI!!! Per accedere a s ( nel codice sotto ) scrivi s, non scrivi *s come faresti con un puntatore. Ma puoi applicare l'aritmetica dei puntatori a questi "riferimenti".
Quindi si, una variabile referente una stringa è un "puntatore" all'area di memoria contigua ( array di caratteri ) contenente la stringa.
E ovviamente puoi avere
C:
char s[] = "Ciao mondo"; // stringa allocata nello stack
char *t = malloc(1024); // stringa allocata nell'heap
strncpy(t, s, strlen(s));
printf("s = %s\n", s+1); // s+1 funziona, come se s fosse un puntatore!!