Newsgroup: it.comp.lang.c
Titolo: struct e malloc in C

Il codice riportato sotto mi genera un errore. Dove sbaglio?
 
Dopo aver digitato il nome e il cognome, la console mi stampa solo il 
cognome.
 
Perchï¿œ mi salta il nome?
 
Grazie
nero23
 
 
 
#include <stdlib.h>
#include <stdio.h>
 
 
int main(void)
{
     typedef struct dati
     {
         char nome[20];
         char cognome[20];
     } dati;
     dati *impiegato;
 
     impiegato = ( dati *) malloc(sizeof( dati)); //alloco la memoria
 
 
     printf("Nome [0 per uscire]: ");
     scanf("%s",impiegato->nome);
 
 
     fflush(stdin); //Svuoto il buffer
 
     printf("Cognome [0 per uscire]: ");
     scanf("%s",impiegato->cognome);
 
 
     free(impiegato); // Dealloco la memoria
 
 
     printf("%s\n",impiegato->nome);
     printf("%s\n",impiegato->cognome);
 
 
     return 0;
}

      printf("%s\n",impiegato->cognome);
[..]
 
Bravo, libera la memoria e dopo usala, mi raccomando ;)
Caro NERO23, personalmente apprezzo molto il tuo sforzo di tenere in vita
questo newsgroup, a me pare evidente che il tuo ï¿œ un "trollismo bonario",
chiamiamolo cosï¿œ. Certo farebbe molto piï¿œ piacere una partecipazione piï¿œ
attiva, piï¿œ fruttuosa e costruttiva, di piï¿œ teste, che non la semplice
presentazione di problemi "ad hoc". Ciao!
 
 
-- 
Vincenzo Mercuri

Che svista :(
Grazie, ora funziona
 
ciao


 [...]
 
 
Ciao  Manlio

 inserisce piÃ¹ di 20 caratteri?
 
 
non sono 19 + \0  caratteri?


Si, con il tuo programma se inserisci oltre 19 caratteri da tastiera fai
crashare il mondo. L'argomento era giï¿œ stato affrontato, puoi uscirne o
usando una funzione tipo 'getline()', come ti avevo detto, oppure utilizzi
una funzione come 'fgets()' con qualche accortezza..
 
 
#include <stdlib.h>
#include <stdio.h>
 
int main(void)
{
     typedef struct
     {
         char nome[20];
         char cognome[20];
     } dati;
 
     dati *impiegato;
     int c = 0;
 
     impiegato = malloc(sizeof(dati));
 
     printf("Nome [0 per uscire]: ");
     fgets(impiegato->nome, 20, stdin);
 
     while((c = getchar()) != '\n');
     printf("Cognome [0 per uscire]: ");
     fgets(impiegato->cognome, 20, stdin);
 
     printf("%s\n", impiegato->nome);
     printf("%s\n", impiegato->cognome);
     free(impiegato);
     return 0;
}
 
NB: la riga
 
	while((c = getchar()) != '\n');
 
ti permette di non includere nella stringa successiva (cioï¿œ
'impiegato->cognome') i caratteri che non possono rientrare nella
stringa 'impiegato->nome'.. almeno cosï¿œ il programma non crasha mai..
 
PS: una svista da parte mia, 'fflush(stdin)' ï¿œ assolutamente vietato,
     porta ad undefined behavior.. 'fflush()' serve solo per gli stream
     in output.
 
-- 
Vincenzo Mercuri

 inserisce piÃ¹ di 20 caratteri?
 
ho risolto con la funzione fgets() (almeno credo....)
 
 
#include <stdlib.h>
#include <stdio.h>
 
 
int main(void)
{
 
     typedef struct
     {
         char nome[20];
         char cognome[20];
     } dati;
 
     dati *impiegato;
 
     impiegato = ( dati *) malloc(sizeof( dati));
 
     printf("Nome [max 19 caratteri]: ");
     fgets( impiegato->nome, 20, stdin );
 
     fflush(stdin); //Svuoto il buffer
 
     printf("Cognome [max 19 caratteri]: ");
     fgets( impiegato->cognome, 20, stdin );
 
 
 
     printf("%s\n",impiegato->nome);
     printf("%s\n",impiegato->cognome);
 
 
     free(impiegato);
 
     return 0;
}
 


 
 
due 'fgets()' successive devono essere gestite con attenzione, se vai
oltre 19 caratteri comunque ti trovi nei pasticci, provare per credere : )
 
-- 
Vincenzo Mercuri


 
Rettifica: con le 'fgets()' il programma non crasherebbe, ma rimarrebbero
nel buffer in input dei caratteri non letti, compreso il carattere '\n'
che verrebbe letto automaticamente alla chiamata successiva di 'fgets()'
che quindi non ti permetterebbe piï¿œ di inserire altro.
 
 
-- 
Vincenzo Mercuri


 
 
Dopo aver inserito  il nome, sono costretto a dare due volte l'invio.
Non succede se elimino l'istruzione while((c = getchar()) != '\n');

 
 
Oppure un semplice:
 
  scanf("%19s",impiegato->nome);
 
Peccato che non sia possibile fare
 
  scanf("%*s", 19, impiegato->nome);
 
cosa che rende la specificazione del numero massimo di caratteri da 
leggere abbastanza scomoda.
 
 
Ciao  Manlio

 altro.
 
Direi che questo comportamento è implementation defined.
Succede leggendo da terminale, a meno di non modificarne il comportamento.
 
 
Ciao  Manlio


Ops, si avevo dimenticato che 'fgets()' quando puo' inserisce il carattere
'\n' all'interno dell'array. Ora, se elimini la riga 'while..' incorri
comunque nel problema che oltre 19 caratteri non puoi andare. Allora il
test si complica un pochino, ma niente di che:
 
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
 
int main(void)
{
     typedef struct {
         char nome[20];
         char cognome[20];
     } dati;
 
     dati *impiegato;
     int c = 0;
 
     impiegato = malloc(sizeof(dati));
 
     printf("Nome [0 per uscire]: ");
     fgets(impiegato->nome, 20, stdin);
 
     if(impiegato->nome[strlen(impiegato->nome) - 1] != '\n')
         while((c = getchar()) != '\n');
 
     printf("Cognome [0 per uscire]: ");
     fgets(impiegato->cognome, 20, stdin);
 
     printf("%s\n", impiegato->nome);
     printf("%s\n", impiegato->cognome);
     free(impiegato);
     return 0;
}
 
Se ci fai caso, per stringhe piccole di caratteri, cioï¿œ che non tocchino
la dimensione 19, 'fgets()' inserisce automaticamente '\n' e te ne accorgi
quando le stampi con 'printf()'.. naturalmente puoi sempre togliere '\n'
dalla stringa, questo perï¿œ lo lascio a te, ï¿œ facile : )
-- 
Vincenzo Mercuri


Lo Standard a proposito della funzione 'int fflush(FILE* stream)' dice
questo (N1256, 7.19.5.2p2):
 
     If stream points to an output stream or an update stream in which
     the most recent operation was not input, the fflush function causes
     any unwritten data for that stream to be delivered to the host
     environment to be written to the file; otherwise, the behavior is
     undefined.
 
con 'stdin' siamo nel caso "otherwise", quindi UB.
 
PS: non so dirti se nel C11 sia cambiato qualcosa.
 
-- 
Vincenzo Mercuri


.. Scusa Manlio, non ti riferivi a fflush(), devo aver dormito
poco stanotte : ) si sono d'accordo con te.
 
-- 
Vincenzo Mercuri


Mi sembra che fgets,quella standard, legga al massimo n -1 caratteri, e 
il puntatore al file è quello relativo all' ultimo carattere letto.
A meno di errori ovviamente, cioè se restituisce null.
Perchè dici che il comportamento è implementation defined ?

 con 'stdin' siamo nel caso "otherwise", quindi UB.
 
 
Ti dispiace spiegarmi cosa si intende per UB?
Con il codice riportato sotto sembra funzionare tutto perfettamente.
Non riesco a spiegarmi perchï¿œ tra la stampa del nome e la stampa del 
cognome, mi salta una riga
esempio
 
Mario
 
Rossi
 
anzichï¿œ
 
Mario
Rossi
 
 
 
 
 
 
 
 
#include <stdlib.h>
#include <stdio.h>
 
 
int main(void)
{
 
     typedef struct
     {
         char nome[20];
         char cognome[20];
     } dati;
 
     dati *impiegato;
 
     impiegato = ( dati *) malloc(sizeof( dati)); //alloco la memoria
 
     printf("Nome [max 19 caratteri]: ");
     fgets( impiegato->nome, 20, stdin ); // Accetta solo  i primi 19 
caratteri
 
     fflush(stdin); //Svuoto il buffer
 
     printf("Cognome [max 19 caratteri]: ");
     fgets( impiegato->cognome, 20, stdin ); // Accetta solo  i primi 19 
caratteri
 
 
     printf("%s\n",impiegato->nome);
     printf("%s\n",impiegato->cognome);
 
 
     free(impiegato); // Dealloco la memoria
 
     return 0;
}

      fflush(stdin); //Svuoto il buffer
[..]
 
Vedo che con te parlare non serve a niente.
 
-- 
Vincenzo Mercuri


 
Questa è bella, ogni tanto ne sento di tutti i colori :)
 
 


Eheh, si era un modo suggestivo per farlo capire, ma diamine, quando
uno è proprio interessato alla singola funzione della libreria standard
c'è una documentazione su internet che non finisce più.. : P
 
-- 
Vincenzo Mercuri


Che compilatore usi perchè la fgets inserisca un newline se la riga 
contiene spazi ?
 

 contiene spazi ?
 
 
Anche se il mio codice potrebbe dare i numeri, come piï¿œ volte mi ï¿œ stato 
fatto notare qui, riesco a inserire una stringa tipo "Mario Rossi"
 


 
Che compilatore usi ?
 


 
 
In molti forum usano l'istruzione
 
while(( getchar()) != '\n');
 
per svuotare il buffer. Un po' diversa dalla tua.


 
Non è questione di compilatore, è proprio il comportamento previsto
dallo Sandard. Una chiamata tipo fgets(stringa, n, stdin) copia in
'stringa' tutti i caratteri che inserisci (spazi compresi) finchè
non succede che: o raggiungi gli n-1 caratteri, o incontra '\n'
oppure EOF. Se ne legge n-1 allora l'unica cosa che puo' fare
è mettere in coda '\0' per chiudere la stringa. Altrimenti
inserisce sia '\n' che '\0'.
 
-- 
Vincenzo Mercuri


Altrimenti intendevo se incontra prima '\n' inserisce
sia '\n' che '\0'. Se incontra EOF invece termina la lettura
e l'inserimento. Uso gcc 4.8.0 comunque.
 
-- 
Vincenzo Mercuri

 Che compilatore usi ?
 
 
codeblocks-12.11mingw

 inserisce sia '\n' che '\0'.
 
Questo giustifica il fatto che tra i due printf aggiunge una riga vuota 
quando la stringa inserita (campo nome) ï¿œ piï¿œ corta di 19 caratteri
 
 


Certo.
 
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
 
int main(void)
{
     typedef struct
     {
         char nome[20];
         char cognome[20];
     } dati;
 
     dati *impiegato;
     int c = 0;
 
     impiegato = malloc(sizeof(dati));
     if (!impiegato)
     {
         fprintf(stderr, "Errore: impossibile allocare %zu "
                 "bytes.\nAlla prossima..\n", sizeof(dati));
         system("PAUSE"); // tanto lo so che sei su Windows
         exit("EXIT_FAILURE");
     }
 
     printf("Inserisci il nome: ");
     fgets(impiegato->nome, 20, stdin);
 
     c = impiegato->nome[strlen(impiegato->nome) - 1];
     if(c != '\n')
     {
         if(c != EOF)
         {
             puts("Errore nel nome: sei andato oltre i 19 caratteri");
             while((c = getchar()) != '\n');
         }
     }
     else
     {
         impiegato->nome[strlen(impiegato->nome) - 1] = 0;
     }
 
     printf("Inserisci il cognome: ");
     fgets(impiegato->cognome, 20, stdin);
 
     c = impiegato->cognome[strlen(impiegato->cognome) - 1];
     if(c != '\n')
     {
         if(c != EOF)
         {
             puts("Errore nel cognome: sei andato oltre i 19 caratteri");
             while((c = getchar()) != '\n');
         }
     }
     else
     {
         impiegato->cognome[strlen(impiegato->cognome) - 1] = 0;
     }
 
     putchar('\n');
     printf("Nome: %s\n", impiegato->nome);
     printf("Cognome: %s\n", impiegato->cognome);
     free(impiegato);
     return 0;
}
 
 
 
-- 
Vincenzo Mercuri

          exit("EXIT_FAILURE");
 
che senza virgolette ï¿œ meglio
 
-- 
Vincenzo Mercuri

Il 18/05/2013 20:53, Vincenzo Mercuri ha scritto:

Mbeh, sono ancora in attesa del bug, non l'hai ancora trovato?
 
-- 
Vincenzo Mercuri

 putchar('\n');
 
 
Qui va anche bene anche il classico printf("\n"); ?

 Qui va anche bene anche il classico printf("\n"); ?
 
No, solo putchar('\n'); va bene, mi dispiace.
 
-- 
Vincenzo Mercuri

 No, solo putchar('\n'); va bene, mi dispiace.
 
 
??

 codeblocks-12.11mingw
 
 
Che funziona secondo gli std quindi è giusto cosi (la riga Mario Rossi 
funziona)
 


Qui pullula di trolls. Anche il mio trolling-against-trolls
è fallito, vi saluto : )
 
-- 
Vincenzo Mercuri


Quello che dici è il comportamento della fgets std
Ma quindi cosa intendi con la  frase che ti riporto sotto ?
 
Gli spazi aggiuntivi che vedi sono
dovuti al fatto che 'fgets()', se trova spazio nell'array di caratteri,
inserisce anche il carattere di newline '\n'.
 
Probabilmente sono io che non ho interpretato correttamente la frase
 


Prima di darmi del troll perchè non leggi la mia risposta a te in un 
altro thread di questo post ?
 


 
Quello è dovuto all' istruzione while((c = getchar()) != '\n')
(che è la stessa cosa di while(getchar() != '\n') in quanto la variabile 
'c' non è usata )
 

 {
 
 
Non mi trovo con questa parte del codice.
Cosi mi dovrebbe segnalare il superamento della lunghezza massima anche 
inserendo solo 19 caratteri...


 
???


Bravo! Hai trovato il bug! Ora meriti una merendina.
 
-- 
Vincenzo Mercuri

 Bravo! Hai trovato il bug! Ora meriti una merendina.
 
Ipotizziamo di aver digitato 19 caratteri. Nel vettore entrano 19 
caratteri + \n, ma manca \0.
 
 

 caratteri + \n, ma manca \0.
 
 
opss...
Digitando 19 caratteri + l'invio, fgets() acquisisce 19 caratteri. 
Quindi perde l'\n. Di conseguenza, non mancherï¿œ mai \0
 
 

c = impiegato->nome[strlen(impiegato->nome) - 1];
 
La funzione strlen() conta anche \n, ma non \0
 
se digito 19 caratteri(e quindi sono nel limite max) o piï¿œ, c 
corrisponde sempre al carattere della posizione 18 del vettore 
nome/cognome. Quindi c non immagazzina il valore \n


Se inserisci una stringa < 19 chars la while attenderà un input da 
stdin, dai l' invio per proseguire e avrai una nuova linea se è questo 
che intendevi
 

 nome/cognome. Quindi c non immagazzina il valore \n
 
intendevo dire impiegato->nome[18] corrisponde al 19 carattere...
 

 Bravo! Hai trovato il bug! Ora meriti una merendina.
 
 
Se limito a 18 il numero massimo di caratteri digitabili, credo di aver 
risolto il problema.
 
Mi confermate?