Puntatori e Array
Se ho capito esattamente, posso utilizzare dei puntatori a un char, al posto di utilizzare ( e qundi allora memoria per nulla) delle stringhe (array di caratteri).
Un array (e quindi, come caso particolare, una stringa) e` qualcosa in piu` di un puntatore: e` un puntatore che punta ad un'area di memoria che e` stata appositamente allocata per contenere dei dati (nel caso delle stringhe, dei caratteri).
Quindi se scriviamo
char stringa[10];
viene allocata un'area di memoria atta a contenere 10 caratteri ed inoltre il simbolo 'stringa' viene considerato come il riferimento a questa area di memoria.
Un puntatore e` solo un riferimento, contiene un indirizzo di un'area di memoria, ma puo` succedere che quell'area di memoria non sia stata assegnata al suo processo: potrebbe essere un'area di memoria del sistema operativo o di altri processi.
Quindi se scriviamo
char *stringa;
stiamo creando solo un puntatore, il cui valore non e` specificato. Se proviamo a scriverci qualcosa, ad esempio con stringa[0]='a'
,
stiamo scrivendo su un'area di memoria su cui non dovremmo scrivere. Se
siamo fortunati e il valore del puntatore mandava ad un'area non
assegnata al mio processo, il sistema operativo segnalera` l'errore.
Se siamo particolarmente sfortunati e il puntatore puntava ad un'area
all'interno del mio processo, potrei non ricevere l'errore ed andare,
magari, a sovrascrivere un'altra variabile (a caso). Nessuno ci segnala
l'errore, il programma funziona a volte si` ed a volte no, ed abbiamo
un errore molto difficile da trovare.
Tutto questo viene confermato (secondo il mio parere) dal seguente
semplice, listato:
#include<stdio.h>
main()
{
struct msg
{
char *p1;
char *p2;
}miei_ptr;
miei_ptr.p1= "Un bel libro sul C";
miei_ptr.p2= "scritto da me";
printf("%s %s\n", miei_ptr.p1,miei_ptr.p2);
}
Questo programma funziona ma le sconsiglio di programmare cosi` (e considerero` un errore se programma cosi` durante l'esame).
Il compilatore scrive in un'area di memoria le stringhe costanti che trova nel codice. Quindi, ci sara` un'area in cui c'e` la stringa "Un bel libro sul C". A p1 viene assegnato il riferimento a quell'area. Stessa cosa per la seconda stringa. Sfortunatamente, su quell'area non si puo` scrivere, per cui modificando il programma come segue, non funziona piu`:
#include<stdio.h>
main()
{
struct msg
{
char *p1;
char *p2;
}miei_ptr;
miei_ptr.p1= "Un bel libro sul C";
miei_ptr.p2= "scritto da me";
miei_ptr.p1[1]='a';
printf("%s %s\n", miei_ptr.p1,miei_ptr.p2);
}
Peggio: su alcuni compilatori potrebbe funzionare e con altri no.
Il problema sorge quando voglio inserire le stringhe da tastiera, ad esempio:
Il listato viene regolarmente compilato, linkato ed eseguito.
Dopo aver inserito il nome ed il cognome, mi da errore.
#include<stdio.h>
main()
{ struct dati
{ char *p_nome;
char *p_cognome;
}persona;
printf("Inserisci il tuo nome: ");
scanf("%s",&persona.p_nome);
printf("Inserisci il tuo cognome: ");
scanf("%s",&persona.p_cognome);
printf("\n");
printf("\n");
printf("****__STAMPA A VIDEO__****\n");
printf("Il tuo nome e':%s\n",persona.p_nome);
printf("\n");
printf("Il tuo cognome e':%s\n",persona.p_cognome);
}
In questo programma ci sono due errori. Il primo e` il parametro della scanf
.
scanf("%s",&persona.p_nome);
significa: "Leggi da tastiera una stringa e salvala nell'area di memoria che comincia all'indirizzo su cui si trova IL CAMPO p_nome DELLA STRUTTURA persona".
persona e` memorizzato nel record di attivazione del main. Supponiamo che sia memorizzato a partire dall'indirizzo 100. Il campo persona.p_nome sara` memorizzato a partire dall'indirizzo 100. Supponiamo che il puntatore occupi 4 byte: il campo persona.p_cognome sara` memorizzato a partire dall'indirizzo 104.
Possiamo rappresentare cosi` il record di attivazione:
main() | (return address, dynamic link, ...) | |||
persona.p_nome persona.p_cognome | 100 104 |
Supponiamo che l'utente inserisca il nome "PIETRO". Ecco come viene memorizzato:
main() | (return address, dynamic link, ...) | |||
persona.p_nome persona.p_cognome |
| 100 104 |
Quindi succede che su p_nome
, che dovrebbe contenere un
indirizzo, noi abbiamo scritto dei caratteri. Nella printf
, chiediamo
al C di interpretare questi caratteri come un puntatore e di stampare
la stringa che si trova a quell'indirizzo. Quindi il C considera 'P'
'I' 'E' 'T' come 4 codici ASCII ed interpreta il numero che viene fuori
come un indirizzo in memoria, poi va a vedere quale stringa c'e` a
quell'indirizzo di memoria. Il sistema operativo impedisce di accedere
a quell'area e termina il programma.
Quindi noi dobbiamo dire alla scanf
di salvare i dati non all'indirizzo in cui è memorizzata
la variabile p_nome
, ma all'indirizzo che e` scritto nella variabile p_nome
scanf("%s",persona.p_nome);
A questo punto, pero` c'e` il secondo errore: consiste nel fatto che
p_nome
è un puntatore, ma non lo abbiamo fatto puntare ad un'area di
memoria in cui il processo poteva scrivere. Quindi la scanf
cerca di
scrivere "PIETRO" in un'area di memoria, ma il sistema operativo le
impedisce di farlo, terminando il programma.
La soluzione e` di dire al compilatore di riservare un'area di
memoria per le stringhe, dichiarando p_nome
e p_cognome
come array e
non come puntatori:
struct dati
{ char p_nome[10];
char p_cognome[10];
}persona;