[C]:Acquisizione caratteri
Ragazzi ho dei seri problemi ad acquisire 2 caratteri.
Allora il prof ci ha detto che si farebbe così:
char s;
printf("inserisci carattere");
scanf("%c",&s);
o se così ci da dei problemi,nello scanf invece che %c mettere %1s.
Noi abbiamo usato solo scanf per acquisire dati quindi non scrivetemi altre cose che non le posso usare...
se dovessi acquisire 2 caratteri come devo fare?secondo quanto mi è stato detto faccio così:
char a,b;
scanf("%1s",&a);
scanf("%1s,&b);
ma se provo a stamparli con printf mi stampa uno spazio seguito dal carattere che ho assegnato a b...perchè?soluzioni?grazie sono in crisi...
Allora il prof ci ha detto che si farebbe così:
char s;
printf("inserisci carattere");
scanf("%c",&s);
o se così ci da dei problemi,nello scanf invece che %c mettere %1s.
Noi abbiamo usato solo scanf per acquisire dati quindi non scrivetemi altre cose che non le posso usare...
se dovessi acquisire 2 caratteri come devo fare?secondo quanto mi è stato detto faccio così:
char a,b;
scanf("%1s",&a);
scanf("%1s,&b);
ma se provo a stamparli con printf mi stampa uno spazio seguito dal carattere che ho assegnato a b...perchè?soluzioni?grazie sono in crisi...
Risposte
È incredibile quanta gente incompetente si metta ad insegnare un linguaggio di programmazione che, a quanto pare, neanche conosce. Eppure il C non è poi tanto complicato come linguaggio, anche se difficile da usare bene. La cosa sconvolgente è che non ho mai incontrato un professore di un corso di base che sapesse decentemente il C. Spero sinceramente di incontrarlo un giorno. Ma veniamo al tuo problema.
Leggere un singolo carattere da file o console è un po' più complicato rispetto a leggere un semplice numero o una stringa: i caratteri vengono infatti gestiti in modo particolare dalla funzione scanf. La differenza principale risiede nel diverso comportamento nei confronti dei caratteri di spazio (come spazi o tabulazioni o a capo...). Normalmente, nella lettura di un numero o di una stringa, gli spazi iniziali vengono ignorati, ma quando si legge un singolo carattere questo non avviene. Se devi leggere un singolo carattere e non ci sono spazi o caratteri di a capo inseriti precedentemente, la lettura non pone alcun problema ed è quindi sufficiente scrivere scanf("%c", &s). Ma quando gli spazi sono presenti, questo codice legge gli spazi invece che il carattere da te voluto. Ecco quindi l'idea del tuo professore di usare "%1s". Quando si legge una stringa infatti, gli spazi vengono ignorati e riesci a leggere il tuo carattere senza dover affrontare direttamente il problema degli spazi. Ma ecco che si presenta un nuovo errore: una stringa di lunghezza uno per le librerie standard NON è un singolo carattere ma un array di due caratteri in cui il secondo è il terminatore di stringa '\0'. scanf si aspetta quindi un array di due char e scrive due valori, il primo è il tuo carattere e il secondo è nella locazione di memoria consecutiva (che può appartenere ad un altra variabile oppure essere oltre i limiti dello stack). Questa operazione è quindi fondamentalmente sbagliata e va evitata a tutti i costi.
Ma c'è un metodo più semplice per risolvere il problema senza far ricorso ad altre funzioni. Devo ammettere che non lo ricordavo la scorsa volta che ho discusso dell'argomento (in effetti non faccio praticamente mai uso di scanf e ho dovuto andare a verificare la sua documentazione). All'interno della stringa che passi a scanf puoi inserire qualsiasi carattere e non solo % seguito dagli identificatori. Se un carattere qualsiasi viene incontrato in questa stringa viene ignorato quando incontrato da scanf (o la funzione termina la lettura nel caso questo carattere non sia presente). Per esempio scanf("%cab%c", &c1, &c2); legge un singolo carattere poi cerca di leggere "ab" dalla console e infine legge il carattere successivo. Ma non tutti i caratteri sono uguali, lo spazio ha infatti un comportamento molto più interessante: legge tutti gli spazi fino al successivo carattere che non lo è. Quindi " %c" fa esattamente quello che fa "%1s" (ignora cioè gli spazi) ma senza il problema di scrivere in una locazione di memoria non valida. Per leggere due carattere e quindi sufficiente scrivere:
P.S. Ovviamente puoi anche unire le due funzioni in una sola scrivendo scanf(" %c %c", &c1, &c2);
Leggere un singolo carattere da file o console è un po' più complicato rispetto a leggere un semplice numero o una stringa: i caratteri vengono infatti gestiti in modo particolare dalla funzione scanf. La differenza principale risiede nel diverso comportamento nei confronti dei caratteri di spazio (come spazi o tabulazioni o a capo...). Normalmente, nella lettura di un numero o di una stringa, gli spazi iniziali vengono ignorati, ma quando si legge un singolo carattere questo non avviene. Se devi leggere un singolo carattere e non ci sono spazi o caratteri di a capo inseriti precedentemente, la lettura non pone alcun problema ed è quindi sufficiente scrivere scanf("%c", &s). Ma quando gli spazi sono presenti, questo codice legge gli spazi invece che il carattere da te voluto. Ecco quindi l'idea del tuo professore di usare "%1s". Quando si legge una stringa infatti, gli spazi vengono ignorati e riesci a leggere il tuo carattere senza dover affrontare direttamente il problema degli spazi. Ma ecco che si presenta un nuovo errore: una stringa di lunghezza uno per le librerie standard NON è un singolo carattere ma un array di due caratteri in cui il secondo è il terminatore di stringa '\0'. scanf si aspetta quindi un array di due char e scrive due valori, il primo è il tuo carattere e il secondo è nella locazione di memoria consecutiva (che può appartenere ad un altra variabile oppure essere oltre i limiti dello stack). Questa operazione è quindi fondamentalmente sbagliata e va evitata a tutti i costi.
Ma c'è un metodo più semplice per risolvere il problema senza far ricorso ad altre funzioni. Devo ammettere che non lo ricordavo la scorsa volta che ho discusso dell'argomento (in effetti non faccio praticamente mai uso di scanf e ho dovuto andare a verificare la sua documentazione). All'interno della stringa che passi a scanf puoi inserire qualsiasi carattere e non solo % seguito dagli identificatori. Se un carattere qualsiasi viene incontrato in questa stringa viene ignorato quando incontrato da scanf (o la funzione termina la lettura nel caso questo carattere non sia presente). Per esempio scanf("%cab%c", &c1, &c2); legge un singolo carattere poi cerca di leggere "ab" dalla console e infine legge il carattere successivo. Ma non tutti i caratteri sono uguali, lo spazio ha infatti un comportamento molto più interessante: legge tutti gli spazi fino al successivo carattere che non lo è. Quindi " %c" fa esattamente quello che fa "%1s" (ignora cioè gli spazi) ma senza il problema di scrivere in una locazione di memoria non valida. Per leggere due carattere e quindi sufficiente scrivere:
char c1, c2; scanf(" %c", &c1); scanf(" %c", &c2);
P.S. Ovviamente puoi anche unire le due funzioni in una sola scrivendo scanf(" %c %c", &c1, &c2);
Ahimé, è vero che è difficile trovare qualche professore che conosca bene il linguaggio, ma mi sento di concedergli almeno le attenuanti generiche: normalmente, magari, il linguaggio lo conosce benissimo ma lo usa poco. Comunque:
Corretto. L'idea del prof è proprio questa: se digiti spazi prima del carattere voluto... ma gli spazi li volevi controllare o no? Ovvero, fanno parte dei possibili input o no?
"Per le librerie standard"? Direi per il linguaggio: ogni stringa è (equivalente a) un array auto di char, terminata da zero.
No, è un errore: scanf non "si aspetta un array di due char", riguardati la documentazione. Copypaste dalla manpage di scanf:
" c Matches a sequence of characters whose length is specified by the maximum field width (default 1); the next pointer must be a pointer to char, and there must be enough room for all the characters (no terminating null byte is added)."
Il consiglio di apatriarca è buono, se non ti interessano leggere i caratteri spazio, aggiungne uno alla stringa del formato. Infatti se dici che "mi stampa uno spazio al posto del carattere" vuol dire che il carattere spazio non ti interessa leggerlo. Giusto?
Normalmente, nella lettura di un numero o di una stringa, gli spazi iniziali vengono ignorati, ma quando si legge un singolo carattere questo non avviene. Se devi leggere un singolo carattere e non ci sono spazi o caratteri di a capo inseriti precedentemente, la lettura non pone alcun problema ed è quindi sufficiente scrivere scanf("%c", &s). Ma quando gli spazi sono presenti, questo codice legge gli spazi invece che il carattere da te voluto. Ecco quindi l'idea del tuo professore di usare "%1s".
Corretto. L'idea del prof è proprio questa: se digiti spazi prima del carattere voluto... ma gli spazi li volevi controllare o no? Ovvero, fanno parte dei possibili input o no?
Quando si legge una stringa infatti, gli spazi vengono ignorati e riesci a leggere il tuo carattere senza dover affrontare direttamente il problema degli spazi. Ma ecco che si presenta un nuovo errore: una stringa di lunghezza uno per le librerie standard NON è un singolo carattere ma un array di due caratteri in cui il secondo è il terminatore di stringa '\0'.
"Per le librerie standard"? Direi per il linguaggio: ogni stringa è (equivalente a) un array auto di char, terminata da zero.
scanf si aspetta quindi un array di due char e scrive due valori, il primo è il tuo carattere e il secondo è nella locazione di memoria consecutiva (che può appartenere ad un altra variabile oppure essere oltre i limiti dello stack). Questa operazione è quindi fondamentalmente sbagliata e va evitata a tutti i costi.
No, è un errore: scanf non "si aspetta un array di due char", riguardati la documentazione. Copypaste dalla manpage di scanf:
" c Matches a sequence of characters whose length is specified by the maximum field width (default 1); the next pointer must be a pointer to char, and there must be enough room for all the characters (no terminating null byte is added)."
Il consiglio di apatriarca è buono, se non ti interessano leggere i caratteri spazio, aggiungne uno alla stringa del formato. Infatti se dici che "mi stampa uno spazio al posto del carattere" vuol dire che il carattere spazio non ti interessa leggerlo. Giusto?
Corretto. L'idea del prof è proprio questa: se digiti spazi prima del carattere voluto... ma gli spazi li volevi controllare o no? Ovvero, fanno parte dei possibili input o no?
In realtà accettare l'inserimento di spazi da console può essere complicato e in generale è meglio se questi caratteri vengono semplicemente ignorati. Quando scanf legge qualcosa dallo standard input si ferma al primo spazio che incontra. Questo spazio rimane presente nel buffer fino alla successiva lettura. Diventa quindi complicato distinguere da uno spazio precedentemente inserito (come il carattere di a capo in una riga precedente) o lo spazio inserito volontariamente. Quando lo spazio è tra i possibili valori inseribili conviene per esempio chiedere l'inserimento del carattere tra virgolette (per esempio ' '). In questo modo si può semplicemente utilizzare scanf(" '%c'", %c) per leggere il carattere con la certezza che sia quello desiderato.
No, è un errore: scanf non "si aspetta un array di due char", riguardati la documentazione. Copypaste dalla manpage di scanf:
" c Matches a sequence of characters whose length is specified by the maximum field width (default 1); the next pointer must be a pointer to char, and there must be enough room for all the characters (no terminating null byte is added)."
A quale manpage ti riferisci? In ogni caso hai frainteso le mie parole (o io mi sono espresso male). In quella citazione dalla manpage si fa riferimento a c, ma io mi riferivo al comportamento di scanf nei confronti della stringa "%1s". In questo caso viene letta una stringa di un carattere dalla console che corrisponde ad un array di due char. LO standard si esprime in questo modo sulla faccenda (nella parte su fscanf ma è uguale):
s Matches a sequence of non-white-space characters. If no l length modifier is present, the corresponding argument shall be a pointer to the initial element of a character array large enough to accept the sequence and a terminating null character, which will be added automatically. If an l length modifier is present, the input shall be a sequence of multibyte characters that begins in the initial shift state. Each multibyte character is converted to a wide character as if by a call to the mbrtowc function, with the conversion state described by an mbstate_t object initialized to zero before the first multibyte character is converted. The corresponding argument shall be a pointer to the initial element of an array of wchar_t large enough to accept the sequence and the terminating null wide character, which will be added automatically.
A quale manpage ti riferisci?
La pagina *nix del manuale della funzione: "man scanf"
In ogni caso hai frainteso le mie parole (o io mi sono espresso male). In quella citazione dalla manpage si fa riferimento a c, ma io mi riferivo al comportamento di scanf nei confronti della stringa "%1s". In questo caso viene letta una stringa di un carattere dalla console che corrisponde ad un array di due char. LO standard si esprime in questo modo sulla faccenda (nella parte su fscanf ma è uguale):
No, avevo capito male io

