Newsgroup: it.comp.lang.c
Titolo: fgets invece di gets

Invece di gets() forse è meglio usare fgets() perché così è possibile 
controllare quanti caratteri vuoi leggere al massimo. ad esempio
char nome[20];
/* leggi al massimo 20 caratteri (compreso '\0' finale) dallo
    standard input (normalmente la tastiera */
fgets(nome,20,stdin);
Ciao

 controllare quanti caratteri vuoi leggere al massimo. ad esempio
esatto, pero' posta nello stesso thread :-)
-- 
Devaraja (Xdevaraja87^gmail^c0mX)
Linux Registerd User #338167
http://counter.li.org

fabio z tessitore wrote:

Grazie mille per le vostre indicazioni, ma adesso mi si pone un altro
problema simile, di seguito trovate il mio semplice esercizio:
#include <stdio.h>
#include <ctype.h>
struct anagrafica {
 char *nome;
 char *cognome;
 char *data_nascita;
 char *residenza;
 struct anagrafica *nextPerson;
};
struct anagrafica* insertPerson(char *nome, char *cognome, char
*data_nascita, char *residenza);
void printPerson();
struct anagrafica *startPtr;
struct anagrafica *previousPtr, *currentPtr, *newPerson;
int main(void){
 int scelta = 1;
 char *nome;
 char *cognome;
 char *data_nascita;
 char *residenza;
 
 while(scelta!=0){
  printf("1. Inserisci nuova persona\n");
  printf("2. Visualizza persone\n");
  printf("3. Esci\n");
  scelta = getc(stdin);
 
  switch(scelta){
   case '1':
    nome = malloc(sizeof(char )*20);
    cognome = malloc(sizeof(char)*20);
    data_nascita = malloc(sizeof(char)*20);
    residenza = malloc(sizeof(char)*20);
 
    printf("Inserie il nome:\n");
    nome = gets(nome);
    printf("Inserire il cognome:\n");;
    cognome = gets(cognome);
    printf("Inserie la data di nascita:\n");
    data_nascita = gets(data_nascita);
    printf("Inserire la residenza:\n");;
    residenza = gets(residenza); 
    insertPerson(nome, cognome, data_nascita, residenza);     
   break;
 
   case '2':
    printPerson();
 
   break;
 
   case '3':
    scelta = 0;
   break;
  }
 }
}
struct anagrafica* insertPerson(char *nome, char *cognome, char
*data_nascita, char *residenza){
 
 if (!startPtr){
  printf("Nessun elemento nella lista anagarafica\n");
  startPtr = malloc(sizeof(struct anagrafica));
  startPtr->nextPerson = NULL;
  startPtr->nome = nome;
  startPtr->cognome = cognome;
  startPtr->data_nascita = data_nascita;
  startPtr->residenza = residenza;
 
 } 
 else{
  printf("startPtr->nome %s\n", startPtr->nome);
  currentPtr = startPtr;
  while(currentPtr!=NULL){
   printf("Sono dentro il while dell'else di insert\n");
   printf("currentptr->nome: %s\n", currentPtr->nome);
   previousPtr = currentPtr;
   printf("prevousPtr: %s\n",previousPtr->nome);
   currentPtr = currentPtr->nextPerson;
  }
 
  newPerson = malloc(sizeof(struct anagrafica));
  newPerson->nome = nome;
  newPerson->cognome = cognome;
  newPerson->data_nascita = data_nascita;
  newPerson->residenza = residenza; 
  newPerson->nextPerson = NULL;
  previousPtr->nextPerson = newPerson;
 
 }  
 
 return newPerson;
}
void printPerson(){
 struct anagrafica *currentPtr;
 currentPtr = startPtr;
 while((currentPtr)){
  printf("Nome: %s\n",currentPtr->nome);
  printf("Cognome: %s\n",currentPtr->cognome);
  printf("Data di nascita: %s\n",currentPtr->data_nascita);
  printf("Residenza: %s\n",currentPtr->residenza);
  printf("\n");
  currentPtr = currentPtr->nextPerson;
 
 }
}
Alla riga: scelta = getc(stdin); attendo che venga inserita l'opzione da
eseguire. Ma l'invio viene intercettato da nome = gets(nome); e quindi
passo direttamente alla riga cognome = gets(cognome); Come ovviare a
questo?
A questo punto, abuso ancora un po' della vostra gentilezza per porvi
quest'ultima domanda.
L'esercizio non fa altro che creare una lista concatenata della struttura
anagrafica.
La struttura è creata dinamicamente e anche i membri della struttura sono
creati dinamicamente (questo perché se creavo dei vettori, accadeva che
tutti i nodi aevano le stesse informazioni dell'ultimo nodo inserito).
La mia domanda è: quando andrò a eliminare un nodo sarà sufficiente fare il
free(puntatoreAllaStruttura) o dovrò anche fare il free per ogni elemento
interno della struttura allocato?
Grazie mille.
Mr. Deckar

 interno della struttura allocato?
Non vorrei sbagliarmi, ma credo che sia necessario chiamare free per 
ogni puntatore prima che per la struttura che li contiene.


 
No, sono sicuro. free() rende di nuovo disponibile la memoria allocata 
qualunque cosa contenga. Quindi i puntatori nome, cognome ecc.. vengono 
distrutti e non hai piu' modo di liberare la memoria puntata da quei 
puntatori (le stringhe di char).


 
Grazie DevaraJa,
allora se ho capito benen deallocando la memoria con il puntatore alla
struttura anche gli oggetti allocati all'interno della stessa vengono
liberati?
Grazie infinite.
Mr. Deckard

 
Il principio è semplice: per ogni 'malloc' ci deve essere una 'free'. Se 
per instanziare una struttura di dati vengono usate N chiamate a 'malloc', 
ci dovranno essere N chiamate a 'free' (non una di più, non una di meno).
Max

Max M. wrote:

Grazie Max,
era il sopsetto che avevo anch'io e che in un primo momento aveva confermato
anche DevaraJa...
Ricapitolando io ho una semplice struct del seguente tipo:
struct anagrafica {
 char *nome;
 char *cognome;
 char *data_nascita;
 char *residenza;
 struct anagrafica *nextPerson;
};
A prescindere se sia il modo più opportuno o meno e rimanendo alla mera
teoria ipotizziamo il seguente scenario:
La seguente struttura serve per implementare una semplice lista di
anagrafiche. Supponiamo che l'elemento x o meglio l'ennesimo puntatore alla
struttura (struct anagrafica *ennesimoAnagarifcaPtr) debba essere eliminato
è scorretto eseguire semplicemente free(ennesimoAnagarifcaPtr)? Per quanto
detto dovrò eseguire prima qualcosa del tipo:
free(ennesimoAnagraficaPtr->nome);
free(ennesimoAnagraficaPtr->cognome);
free(ennesimoAnagraficaPtr->data_nascita);
free(ennesimoAnagraficaPtr->residenza);
e poi...
free(ennesimoAnagraficaPtr);
Giusto?
Grazie mille per la disponibiltà.
Mr. Deckar
 

Mr. Deckard ha scritto:

No. free() fa in modo che la memoria puntata dall'argomento che passsi 
ritorni disponibile per usi successivi, cioe' la dealloca.
-------------------------------------------------------
                    ------>"stringa1"
                    |   ---->"stringa2"
                    |  |   --->"stringa3"
                    |  |  |   ___>"stringa4"
                    |  |  |  |
[memoria libera..][p1 p2 p3 p4][memoria libera....]
^
|
|____p_struct
-------------------------------------------------------------
p1..p4 sono puntatori a char, mentre p_struct e' un puntatore a una 
struttura anagrafica. La chiamata a free(p_struct) fa questo:
----------------------------------------
                      "stringa1"
                           "stringa2"
                                "stringa3"
				"stringa4"
[memoria libera..][memoria libera...][memoria libera....]
p_struct--->NULL
----------------------------------------
Cioe' dice che la memoria che conteneva quella struttura e' 
riutilizzabile. Ma quelle stringhe rimangono sempre in memoria, solo che 
sono irraggiungibili e quindi inutili. Se invece chiami free per ogni 
stringa e poi per la struttura contenitore ottieni questo:
-----------------------------------------
		[memoria libera..]
		   [memoria libera..]
		     [memoria libera..]
			[memoria libera..]
[memoria libera..][memoria libera..][memoria libera..][memoria libera..]
p_struct--->NULL
-----------------------------------------
Che e' la situazione migliore. Ricorda anche che free non imposta a 0 la 
memoria liberata, si limita a renderla disponibile per usi futuri. Spero 
che i disegni siano venuti bene :-)
e ho appena finito fallout2 ! :-D



cosi' va meglio


DevarajA wrote:

Eh, eh, grazie mille!!! I tuoi grafici sono bellissimi!!!
Sì avevo capito... Adesso mi hai confermato, con limpidezza, il tutto!
Grazie per la pazienza e la disponibilità!
Mr. Deckard

 puntatori (le stringhe di char).
Questa e` una boiata. fgets non rialloca nulla. fgets restituisce il
puntatore passatogli oppure NULL in caso di fallimento. Quindi bisogna
scrivere:
char *p = malloc(123);
char *ret = fgets(p, 123, stdin);
if (!ret)
        errore();
free(p);
Luca
-- 
Home: 
http://kronoz.cjb.net
Se non sei parte della soluzione, allora sei parte del problema.

 Ciao
... se voglio leggere tutta una stringa
su una riga ma non sň di quanti caratteri
e composta, come si puň fare?
Grazie
Ciao

 Ciao
Devi comunque dare un limite massimo per allocare la memoria prima di 
leggere la stringa
char str[128];
oppure
char* p;
p = malloc(sizeof(char[128]));
così tanto vale leggere solo un numero di caratteri tale da non andare 
in overflow.
fgets(str,128,stdin);
---------------------------------
se la stringa deve essere arbitrariamente lunga devi trovare un 
algoritmo per fare questo. per esempio leggi una parte della stringa e 
se non è ancora finita leggi ancora (eventualmente allocando altra memoria)
spero sia tutto chiaro
ciao

 
ovvero p=malloc(128) perche' sizeof(char)==1 sempre :-)


 
http://counter.li.org
... non và a leggere fino sulla riga sucessiva,
ovvero a capo?
Grazie a tutti
Ciao

 
malloc non legge niente, riserva solo della memoria. Se invece ti 
riferisci a quello che ho postato prima su scanf, no legge fino a quando 
trova '\n'. Ora che ci penso questo non e' quello che voleva l'OP, 
perche' scanf lascia il carattere di stop sullo stdin. In questo modo 
viene letta dalla prossima funzione di lettura e non si risolve il 
problema originale. Sembra allora che la soluzione migliore sia gets o 
fgets, e se proprio vogliamo usare quella scanf bisogna mettere un 
getc(stdin) subito dopo.


 
Esatto DevarJa,
io mi sono trattenuto a scrivere sul gruppo, ma non sono arrivato a molto...
Adesso proverò a mettere un getc(stdin), puoi spiegarmi un po' meglio la
questione?
Mr. Deckar

Mr. Deckard ha scritto:

Le funzioni di input prelevano i caratteri dallo standard input (o altro 
stream), ma non prelevano tutto: una volta che e' stato letto il numero 
specificato di caratteri, o e' stato letto un certo carattere o si e' 
verificata un'altra condizione particolare, la funzione in questione 
smette di leggere e tutti i caratteri successivi all'ultimo letto 
rimangono sullo stream specificato. Nel caso di scanf("%[^\n]",buf); lo 
stream e' stdin, cioe' lo standard input. La stringa di formato 
specifica che al termine della lettura buf dovra' contenere quello che 
e' stato trovato sullo standard input fino al primo '\n' escluso. Quindi 
scanf legge un carattere, se non e' '\n' lo mette in buf. Quando invece 
il carattere letto e' '\n' lo rimette sullo standard input. Una 
successiva chiamata a una funzione di input trovera' questo '\n' come 
primo carattere, cosa che con ogni probabilita' la fara' terminare 
avendo letto solo quello.
stdin: [stringa di prova\nz.......]
buf:   [.................]
	chiamata a scanf("%[^n]"buf);
stdin: [\nz............] <<nota che '\n' e' rimasto
buf:   [stringa di prova\0]
	altra funzione di input chiamata:
	es: x=getc(stdin);
stdin: [z....]
buf:   [stringa di prova\0]
x:     ['\n']
In questo caso getc va a prelevare il primo carattere che trova sullo 
stdin, che a contrario di quanto potrebbe sembrare non e' quella z che 
avevo messo dopo la stringa, ma il '\n' che scanf non ha "mangiato".
Questo problema non si verifica con gets e fgets perche' eliminano il 
'\n' dallo stream da cui leggono (ma non lo mettono sulla stringa!)


DevarajA wrote:

OK ma allora perché invece di scrivere scanf("%[^\n]",buf), non scrivere
scanf("%[\n]",buf), così non dovrebbe accettare anche accettare il newline?
senza poi creare problemi alla funzione successiva di lettura...
Proverò ancora, ma ieri avevo avuto dei problemi anche con gets, ecc. ora
vedo a mente lucida.
Grazie ancora.
Mr.Deckard

 
Che problemi?


Giusto per ricapitolare, dopo aver effettuato una lettura con scanf() e' 
meglio ripulire il buffer con qualcosa del tipo:
int c;
/* leggi tutti i caratteri rimanenti sullo stream */
while ( (c=getchar()) != '\n' && c != EOF )
	;
se invece preferite usare fgets() (e non gets() per il mancato controllo 
sul numero di caratteri) si puo' fare qualcosa del genere:
fgets(stringa,num_caratteri,stdin);
funzione_che_analizza_la_stringa(stringa);
se e' tutto ok (cioe' se la stringa e' come la vogliamo noi)
	/* possiamo leggere anche eventuali numeri con sscanf() */
	sscanf(stringa,"%d",&mio_intero);
ciao

va beh, ma un modo per far
leggere a fgets tutta una str fino al carattere \n
c'č? senza impostare un char di x caratteri.
Ciao

 
in effeti utilizzando questa soluzione tutto va a gonfie vele...
 Mr. Deckard

 
fgets lo fa gia' in automatico: legge x caratteri ma si ferma se prima 
trova EOF o '\n'. Quindi basta mettere un limite molto alto e sei quasi 
sicuro che si ferma quando trova '\n', ovviamente pero' devi allocare 
abbastanza memoria per gestire l'eventualita' che un utente inserisca 
una stringa della lunghezza massima. Quindi rispondendo in un modo piu' 
lineare alla tua domanda: no, non e' possibile far tenere SOLO quel 
comportamento a fgets. Se vuoi leggere un numero di caratteri che non 
hai modo di conoscere devi usare scanf("%[^\n]\n",buf) o gets(buf) pero' 
in entrambi i modi non sei al sicuro da un eventuale buffer overflow. Se 
mi vengono altre idee le posto...


 
Sbagliato. gets legge il '\n' senza metterlo sulla stringa, ma fgets lo 
mette anche sulla stringa



lavorando con la libreria standard non esiste modo 'semplice' di evitare 
il buffer overflow.
L'unica maniera che mi viene in mente e' di utilizzare all'interno di 
una funzione fgetc() iterato ed allocare dinamicamente il buffer via via 
che cresce ( magari in pezzi di 10 o 20 caratteri invece che uno alla 
volta ) con realloc().
qualcosa del tipo: ( non mi guardate lo stile e gli errori )
#define CHUNK 10
char *fgetslong(FILE *f) {
char * tp;
int count , size;
char c;
	tp = malloc(CHUNK);
	size=CHUNK;
	count=0;
	c =fgetc(f);
	while ( c!='\n' && c!=EOF ) {
		if (count==size) { // se lo spazio e' finito
			size += CHUNK; // allarga il buffer
			tp = realloc(tp,size);
		}
		tp[count++]=c;
	}
	if (count==size) {	// serve a far spazio per il NUL
		size += 1;
		tp = realloc(tp,size);
	}
	tp[count]=0;
	return tp;
}
P.S: questa soluzione non si puo' applicare a buffer statici e bisogna 
comunque ricordarsi di deallocare il buffer ritornato con free().
PP.SS: a rigori realloc o malloc possono fallire ....
PPP.SSS: la funzione e' stata scritta di getto provatela prima di usarla

     }
while( (c=fgetc(f))!='\n' && c!=EOF ){


 
giusto, anzi giustissimo ho saltato il secondo fgetc(f) alla fine del 
while(){...}
mio gusto personale non amo molto la maniera 'standard' del C di 
mischiare assegnazioni e test mi confonde.
e poi avevo avvertito che andava controllato

 e poi avevo avvertito che andava controllato
e' quello che ho fatto :-)


 
Ops.. non avevo capito bene la tua richiesta. Si, hai ragione, quelle 
funzioni non ti mettono al riparo da quello che dici ma basta fare 
qualcosa tipo stringa[len-1]=0; dopo ogni lettura e sei a posto.
