Discussione:
[NEWBIE] Problema con scanf
(troppo vecchio per rispondere)
Co$t@ntino
2007-11-24 14:37:51 UTC
Permalink
Ciao a tutti,
È un po' di tempo che sto cercando di imparare il C. Mentre tentavo di
fare un programmino con dei cicli for ho notato una strana anomalia in
scanf. In poche parole le prime due istruzioni funzionano correttamente,
ma l'ultima viene saltata. Ecco il pezzo di codice incriminato:

#include <stdio.h>

int main(void)
{
int numero1,numero2;
char carattere;

printf("Inserisci un numero: ");
scanf("%d",&numero1);
printf("Inserisci un altro numero: ");
scanf("%d",&numero2);
printf("Inserisci un carattere: ");
scanf("%c",&carattere); //Istruzione che viene saltata
printf("Carattere: %c\n",carattere);
return 0;
}

Ed ecco l'output che mostra in shell:
$ cc prova.c -Wall
$ ./a.out
Inserisci un numero: 3
Inserisci un altro numero: 4
Inserisci un carattere: Carattere:

Compilato usando gcc 4.1.2 su Debian Sid. GNU Emacs come editor.
Ho provato anche con "carattere = getchar" ma non funziona. Invece
usando %s come formattatore di campo nello scanf sembra andare, ma alla
fine mi da un errore di segmentazione: segmentation fault (core dumped).
Cos'è che non funziona?

Ciao, Costantino
--
Linux non è un sistema operativo, ma un modo diverso per affrontare la
propria esistenza.
http://linuxrevenge.wordpress.com/
Powered by:
_____ _ _ _____ _ _
| __ \ | | (_) / ____(_) | |
| | | | ___| |__ _ __ _ _ __ | (___ _ __| |
| | | |/ _ \ '_ \| |/ _` | '_ \ \___ \| |/ _` |
| |__| | __/ |_) | | (_| | | | | ____) | | (_| |
|_____/ \___|_.__/|_|\__,_|_| |_| |_____/|_|\__,_|

linux 2.6.23.1-handmaded
Andrea Laforgia
2007-11-24 17:52:14 UTC
Permalink
Post by Co$***@ntino
È un po' di tempo che sto cercando di imparare il C. Mentre tentavo di
fare un programmino con dei cicli for ho notato una strana anomalia in
scanf.
Se avessi ricevuto mezzo centesimo per ogni volta che ho consigliato ai
"newbie" di buttare via la scanf(), preferendo fgets(), oggi sarei
miliardario. Devi intervallare le lettura con scanf() da un ciclo che
svuoti il buffer di tastiera. Ti consiglio di scrivere una funzione:

void flushKeybBuff() {
while (getchar()!='n')
;
}

e invocarla dopo ogni scanf().
L'alternativa è, appunto, usare fgets() (che richiede un pochetto di
lavoro in più, ma è più sicura e meno problematica).
--
questo articolo e` stato inviato via web dal servizio gratuito
http://www.newsland.it/news segnala gli abusi ad ***@newsland.it
Co$t@ntino
2007-11-24 19:03:23 UTC
Permalink
Post by Andrea Laforgia
Post by Co$***@ntino
È un po' di tempo che sto cercando di imparare il C. Mentre tentavo di
fare un programmino con dei cicli for ho notato una strana anomalia in
scanf.
Se avessi ricevuto mezzo centesimo per ogni volta che ho consigliato ai
"newbie" di buttare via la scanf(), preferendo fgets(), oggi sarei
miliardario.
LOL! In effetti avrei dovuto guardare un po' di più prima di postare...
Post by Andrea Laforgia
Devi intervallare le lettura con scanf() da un ciclo che
void flushKeybBuff() {
while (getchar()!='n')
;
}
e invocarla dopo ogni scanf().
L'alternativa è, appunto, usare fgets() (che richiede un pochetto di
lavoro in più, ma è più sicura e meno problematica).
Si...ok.
Ho letto la parte relativa fgets sia sul K&R, sia sul manuale che sto
usando (Linguaggio C - McGraw-Hill) e ho lurkato un po' per il ng. Dice
che è una variante di gets, implementata perché gets non controlla la
lunghezza dei dati in input e potrebbe causare buffer overflow. Right?
Il problema è che io sono proprio alle prime armi e non saprei proprio
come usarla XD. Non è che potresti postarmi il programmino fatto con fgets?

Grazie mille!
Ciao, Costantino


P.S. Sarebbe meglio usare anche fputs al posto printf?
--
Linux non è un sistema operativo, ma un modo diverso per affrontare la
propria esistenza.
http://linuxrevenge.wordpress.com/
Powered by:
_____ _ _ _____ _ _
| __ \ | | (_) / ____(_) | |
| | | | ___| |__ _ __ _ _ __ | (___ _ __| |
| | | |/ _ \ '_ \| |/ _` | '_ \ \___ \| |/ _` |
| |__| | __/ |_) | | (_| | | | | ____) | | (_| |
|_____/ \___|_.__/|_|\__,_|_| |_| |_____/|_|\__,_|

linux 2.6.23.1-handmaded
Andrea Laforgia
2007-11-24 19:34:44 UTC
Permalink
Post by Co$***@ntino
Ho letto la parte relativa fgets sia sul K&R, sia sul manuale che sto
usando (Linguaggio C - McGraw-Hill) e ho lurkato un po' per il ng. Dice
che è una variante di gets, implementata perché gets non controlla la
lunghezza dei dati in input e potrebbe causare buffer overflow. Right?ù
Esatto.
Post by Co$***@ntino
Il problema è che io sono proprio alle prime armi e non saprei proprio
come usarla XD. Non è che potresti postarmi il programmino fatto con fgets?
E' facile scrivere una funzione basata su fgets() (vado al volo, senza
compilare):

#include <stdlib.h>
#include <string.h>

char *readString(const size_t maxDim) {
char *result = malloc(maxDim+1);
if (result) {
size_t len;
fgets(result, maxDim, stdin);
len = strlen(result);
if (result[len-1] == 'n')
result[len-1] = '
Co$t@ntino
2007-11-24 20:08:50 UTC
Permalink
Post by Andrea Laforgia
Post by Co$***@ntino
Ho letto la parte relativa fgets sia sul K&R, sia sul manuale che sto
usando (Linguaggio C - McGraw-Hill) e ho lurkato un po' per il ng. Dice
che è una variante di gets, implementata perché gets non controlla la
lunghezza dei dati in input e potrebbe causare buffer overflow. Right?ù
Esatto.
Post by Co$***@ntino
Il problema è che io sono proprio alle prime armi e non saprei proprio
come usarla XD. Non è che potresti postarmi il programmino fatto con fgets?
E' facile scrivere una funzione basata su fgets() (vado al volo, senza
#include <stdlib.h>
#include <string.h>
char *readString(const size_t maxDim) {
char *result = malloc(maxDim+1);
if (result) {
size_t len;
fgets(result, maxDim, stdin);
len = strlen(result);
if (result[len-1] == 'n')
result[len-1] = '
ehm...
Quando dicevo alle prime armi intendevo veramente alle prime armi!
Ovvero...non so cosa siano puntatori, dichiarazioni di funzioni,
stringhe, array e compagnia bella.
Vabbè, fa niente, rinvio questo stupido programmino a quando sarò più
esperto.

Grazie per l'aiuto!

Ciao, Costantino
--
Linux non è un sistema operativo, ma un modo diverso per affrontare la
propria esistenza.
http://linuxrevenge.wordpress.com/
Powered by:
_____ _ _ _____ _ _
| __ \ | | (_) / ____(_) | |
| | | | ___| |__ _ __ _ _ __ | (___ _ __| |
| | | |/ _ \ '_ \| |/ _` | '_ \ \___ \| |/ _` |
| |__| | __/ |_) | | (_| | | | | ____) | | (_| |
|_____/ \___|_.__/|_|\__,_|_| |_| |_____/|_|\__,_|

linux 2.6.23.1-handmaded
Andrea Laforgia
2007-11-25 23:19:20 UTC
Permalink
Post by Co$***@ntino
Quando dicevo alle prime armi intendevo veramente alle prime armi!
Scusa, ma newsland taglia i messaggi perché interpreta le sequenze di
escape (bah.. che sistema balordo di gestire i newsgroup).
La funzione corretta, comunque, la trovi nel thread sul reversing di
una stringa a cui ho risposto.
Co$t@ntino
2007-11-26 14:20:06 UTC
Permalink
Post by Andrea Laforgia
Post by Co$***@ntino
Quando dicevo alle prime armi intendevo veramente alle prime armi!
Scusa, ma newsland taglia i messaggi perché interpreta le sequenze di
escape (bah.. che sistema balordo di gestire i newsgroup).
Concordo. Quando non posso accedere via nntp uso Google Groups.
Post by Andrea Laforgia
La funzione corretta, comunque, la trovi nel thread sul reversing di
una stringa a cui ho risposto.
Trovata.
char *getString(size_t sz) {
char *result = malloc(sz+1);
if (result) {
size_t len;
fgets(result, sz+1, stdin);
len=strlen(result);
if (result[len-1]=='\n')
result[len-1]='\0';
}
return result;
}

Il problema è che non sapendo minimamente cosa fa e come funziona mi
scoccia includerla nei miei programmi. Comunque visto che
flushKeybBuff() funziona alla grande per ora uso quella. Prendo atto del
consiglio che metterò in pratica quando riuscirò a capire il source.
:D

Grazie, Costantino
--
Linux non è un sistema operativo, ma un modo diverso per affrontare la
propria esistenza.
http://linuxrevenge.wordpress.com/
Powered by:
_____ _ _ _____ _ _
| __ \ | | (_) / ____(_) | |
| | | | ___| |__ _ __ _ _ __ | (___ _ __| |
| | | |/ _ \ '_ \| |/ _` | '_ \ \___ \| |/ _` |
| |__| | __/ |_) | | (_| | | | | ____) | | (_| |
|_____/ \___|_.__/|_|\__,_|_| |_| |_____/|_|\__,_|

linux 2.6.23.1-handmaded
Andrea Laforgia
2007-11-26 14:36:40 UTC
Permalink
Post by Co$***@ntino
char *getString(size_t sz) {
Il parametro passato è la lunghezza della stringa da leggere.
Se vuoi una stringa da 10 caratteri massimo, gli passi 10.
Il risultato è un puntatore alla memoria allocata dinamicamente (vedi la
malloc() sotto) e contenente la stringa letta.
Post by Co$***@ntino
char *result = malloc(sz+1);
Alloca dinamicamente spazio per il risultato, in base al size passato.
Ovviamente aggiunge 1, per il terminatore nullo.
Post by Co$***@ntino
if (result) {
Se l'allocazione è andata a buon fine.
Post by Co$***@ntino
size_t len;
Dichiara la variabile che conterrà la lunghezza della stringa letta.
In C, la dichiarazione di variabili nel mezzo del codice è fattibile solo
a inizio di un compound statement (in C++, invece, è possibile dichiarare
variabili ovunque).
Post by Co$***@ntino
fgets(result, sz+1, stdin);
Legge dall'input (stdin) la stringa in result.
Il parametro sz+1 specifica la lunghezza in byte del buffer. E' il valore
massimo, cioè oltre quella dimensione non è possibile leggere. Questo
rende la fgets() sicura da buffer overflow.
Post by Co$***@ntino
len=strlen(result);
Memorizza la lunghezza della stringa letta in len (nonostante la
dimensione massima passata potrebbe essere sz=50, il numero di caratteri
letti potrebbe essere inferiore).
Post by Co$***@ntino
if (result[len-1]=='n')
result[len-1]='
Rocky3
2007-11-26 09:40:47 UTC
Permalink
Post by Andrea Laforgia
void flushKeybBuff() {
while (getchar()!='n')
;
}
e invocarla dopo ogni scanf().
Sorry per la domanda banale forse ma... questo ciclo cosa fa? Controlla che
il valore di getchar() sia diverso da n... anche se forse intendevi \n.
Quando è uguale a \n, si passa alle istruzioni successive?
Anche pensando alla tua risposta al mio precedente thread, ho cercato di
scrivere questo programma semplice:

#include <stdio.h>
#include <stdlib.h>

int main()
{
char string1, string2;
printf("Inserisci il carattere da memorizzare in string1\n");
scanf("%c", &string1);
void flushKeybBuff();
printf("Inserisci il carattere da memorizzare in string2\n");
scanf("%c", &string2);
printf("string1 vale %c e string 2 vale %c\n", string1, string2);
system("PAUSE");
return 0;
}

void flushKeybBuff() {
while (getchar()!='n')
;
}

Dopo aver inserito il valore per string1 e schiacciato invio, il programma
mi passa alla seconda printf, poi non attende per la scanf successiva, e
stampa la terza printf senza, ovviamente, nessun valore per string2.
Se invece inserisco, al posto di void flushKeybBuff(); semplicemente
getchar(); allora funziona.
Perché?

Rocky3
--
"Il sapere e la ragione parlano, l'ignoranza ed il torto urlano".

Arturo Graf / Indro Montanelli / Anonimo
Andrea Laforgia
2007-11-26 10:53:53 UTC
Permalink
Post by Rocky3
Sorry per la domanda banale forse ma... questo ciclo cosa fa? Controlla
che il valore di getchar() sia diverso da n... anche se forse intendevi n.
Purtroppo newsland, dal quale posto quando sono a lavoro, tronca le
sequenze di escape e a volte gli interi listati. Ovviamente quello che
avevo scritto non è "n", ma la sequenza di escape di a capo. Non posso
riscriverla, sennò viene ugualmente troncata :-)

Quel ciclo serve a leggere dal buffer di tastiera, fino a che non si
incontra un new-line. Il che significa svuotare, di fatto, il buffer di
tastiera, dopo una scanf(). Si presuppone, ovviamente, che l'utente abbia
premuto invio per confermare il valore in ingresso. Questo "flushing" è
necessario, perché altrimenti i dati rimasti nel buffer, dopo la prima
scanf(), diventano dati in ingresso per la seconda scanf(). E così via.
Post by Rocky3
char string1, string2;
printf("Inserisci il carattere da memorizzare in string1n");
scanf("%c", &string1);
Bada che io parlo di lettura di stringhe, non di caratteri singoli.
Post by Rocky3
void flushKeybBuff();
Questa invocazione è sbagliata: togli quel void.
--
questo articolo e` stato inviato via web dal servizio gratuito
http://www.newsland.it/news segnala gli abusi ad ***@newsland.it
Loading...