Salta ai contenuti. | Salta alla navigazione

Strumenti personali

Puntatori e Array

importante

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:

 #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);
}
Il listato viene regolarmente compilato, linkato ed eseguito. Dopo aver inserito il nome ed il cognome, mi da errore.

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
  'P' 'I' 'E' 'T'  
  'R' 'O' '\0'  
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;