KR ciclo while con funzione getchar (e domanda sul main)

di il
7 risposte

KR ciclo while con funzione getchar (e domanda sul main)

Scusate, nel primo capitolo si fa un esempio con un ciclo che legge i caratteri usando la getchar e si interrompe quando incontra il valore EOF.
Utilizzando il codice del libro però il programma non esce mai dal ciclo!

#include <stdio.h>

main()
{
    int c;
    while ((c=getchar())!=EOF)
        putchar(c);

    printf("\n\nSei uscito dal ciclo, il valore di EOF è %d", c);
}
Inoltre avrei una piccola domanda. Vedo che sul K&R non si specifica il tipo int per il main, nn si scrive void tra le parentesi e non si aggiunge return 0 alla fine (almeno in questi primi esempi che ho visto!).
Siccome leggevo una guida che insisteva molto su queste cose (esplicitamente), mi chiedevo voi cosa ne pensavate.

7 Risposte

  • Re: KR ciclo while con funzione getchar (e domanda sul main)

    Per terminare quel ciclo devi esplicitamente introdurre il valore EOF da tastiera. In un terminale, generalmente, si ottiene tale carattere con la combinazione di tasti CTRL+Z. In un terminale DOS/Windows è inoltre possibile immettere direttamente il valore ASCII corrispondente tenendo premuto ALT e digitando le cifre (in questo caso 0, 2 e 6) sul tastierino numerico.
    A ciò deve comunque fare seguito, in questo caso specifico, la pressione di INVIO.

    Nello standard ANSI/INCITS/ISO familiarmente denominato C89 (anno di promulgazione ANSI) o C90 (anno del recepimento da parte di ISO) e rivisto nel 1995 (al quale si riferisce in modo univoco e deterministico il K&R) il tipo di default è int, e come tale può essere omesso dalla dichiarazione del main().
    Allo stesso modo, non è sempre strettamente necessario specificare il void per le funzioni prive di parametri, e nel caso del main quando non si intenda fare uso dei parametri da command line. Tuttavia, è opportuno farlo come regola di stile.

    Solo negli standard successivi (C99, C11) sono state introdotte variazioni a queste fondamentali regole, che rendono fortemente consigliabile essere massimamente espliciti nella dichiarazione del main(). Peraltro, qualsivoglia editor minimamente avanzato consente di predisporre macro e templates appositi, per ottenere il risultato desiderato con il minimo di digitazione.

    La restituzione di un valore convenzionale (tipicamente EXIT_SUCCESS definito in stdlib.h) al termine del main è comunque sempre opportuna per i programmi in ambiente PC, quale che sia il sistema operativo installato. La sua omissione è giustificata (e necessaria) solo in contesti che al momento non ti interessano minimamente.


    Sarà comunque bene chiarire subito che, ad onta dei troppi filoneisti che circolano da ambo i lati di cattedre e cadreghe dirigenziali, il numero di compilatori conformi a C89/C95 è superiore di un intero ordine di grandezza rispetto ai compilatori capziosamente aderenti ad ogni minimo aspetto dello standard C99 o, peggio ancora, del C11 (in pratica appena promulgato, visti i tempi geologici di adeguamento). Se ti interessa solo il mainstream, incontrerai un certo numero di compilatori C99-compliant, con qualche distinguo: ma fuori da questa rassicurante sandbox ti aspetta una jungla di incongruenze, soluzioni strettamente proprietarie, incompatiblità, stranezze e quant'altro. Un mondo ben diverso dai rassicuranti Python, Ruby o Tcl...
  • Re: KR ciclo while con funzione getchar (e domanda sul main)

    M.A.W. 1968 ha scritto:


    Per terminare quel ciclo devi esplicitamente introdurre il valore EOF da tastiera. In un terminale, generalmente, si ottiene tale carattere con la combinazione di tasti CTRL+Z. In un terminale DOS/Windows è inoltre possibile immettere direttamente il valore ASCII corrispondente tenendo premuto ALT e digitando le cifre (in questo caso 0, 2 e 6) sul tastierino numerico.
    A ciò deve comunque fare seguito, in questo caso specifico, la pressione di INVIO.
    Ah ok, avevo capito invece fosse un valore restituito in automatico dalla getchar, così come ad un certo punto si ritrova il carattere di new-line in input dopo aver premuto invio, avevo supposto (male) che dopo il carattere di new-line la getchar restituisse quel valore.

    Varie volte ho usato proprio il carattere '\n' come controllo anzichè EOF, è una "brutta" pratica? L'unica cosa che ho notato (oltre al fatto di non poter premere invio ) è che in input mi rimangono poi dei caratteri "fantasma" che devo tenere in conto ed eliminare in qualche modo se faccio uso ancora della getchar, con l'EOF come controllo si ha lo stesso problema?
    Nello standard ANSI/ISO/INCITS familiarmente denominato C89 (anno di promulgazione ANSI) o C90 (anno del recepimento da parte di ISO) al quale si riferisce in modo univoco e deterministico il K&R il tipo di default è int, e come tale può essere omesso dalla dichiarazione del main(). Allo stesso modo, non è strettamente necessario specificare il void per le funzioni prive di parametri, e nel caso del main quando non si intenda fare uso dei parametri da command line.

    Solo negli standard successivi (C99, C11) sono state introdotte variazioni a queste fondamentali regole.

    La restituzione di un valore convenzionale (tipicamente EXIT_SUCCESS definito in stdlib.h) al termine del main è comunque sempre opportuna per i programmi in ambiente PC, quale che sia il sistema operativo installato. La sua omissione è giustificata (e necessaria) solo in contesti che al momento non ti interessano minimamente.
    Che non fossero condizioni formalmente illegali mi era chiaro, cercavo appunto di capire se si fossero trattati di consigli (e in tal caso se fossero buoni) oppure di regole introdotte negli standard successivi e a cui è meglio farci l'abitudine. Per curiosità, dove posso reperire gli standard C99 e C11 (almeno per tenerli da parte una volta finito il libro)?

    EDIT
    Premesso che so che in sostanza sono equivalenti, mi consigli di usare return oppure exit alla fine del main?
  • Re: KR ciclo while con funzione getchar (e domanda sul main)

    Per i caratteri fantasma e la pulizia del buffer di I/O, leggi questo e dai un'occhiata a questo classico esempio:
    
    void clearKeyboardBuffer() 
    {
        char ch;
        do 
        {
            ch = getchar();
        } while (('\n' != ch) && (EOF != ch));
        /* Alternativa concisa: while (((ch = getchar()) != '\n') && (ch != EOF)); */
    }
    


    EDIT
    Sul corretto uso di return ed exit, leggi invece questo.

    Come aspirante programmatore, non hai alcun bisogno di consultare direttamente uno standard (errore fin troppo comune nella didattica), che nasce unicamente per i progettisti di compilatori e in subordine per porting di vasto respiro. Le tue Bibbie, oltre al K&R, sono i manuali del tuo compilatore preferito e le guide di stile segnalate in bibliografia (tra le quali il Kernighan & Pike, per cominciare).


    PS: a proposito di possibile ambiguità referenziale e vizio smodato di costruire paratassi troppo ramificate stile LISP ma con meno parentesi.

    M.A.W. 1968 ha scritto:


    Nello standard ANSI/INCITS/ISO familiarmente denominato C89 (anno di promulgazione ANSI) o C90 (anno del recepimento da parte di ISO) e rivisto nel 1995 (al quale si riferisce in modo univoco e deterministico il K&R)...
    L'edizione originale del K&R si ferma al C89/C90, le poche modifiche apportate dalla minor revision del 1995 sono posteriori a tale edizione e ovviamente non sono menzionate né nell'orignale inglese, né nella traduzione italiana Jackson Libri. Il soggetto implicito di "al quale si riferisce..." è ovviamente "[lo] standard ANSI/ISO/INCITS [...] C89".
  • Re: KR ciclo while con funzione getchar (e domanda sul main)

    Perdonami la banalità, ma c'è una cosa che ancora non mi è chiara. Quando eseguo questo algoritmo
    
    #include <stdio.h>
    
    int main(void)
    {
        int ch, spazi = 0, tabulazioni = 0, newline = 0;
    
        while ( (ch = getchar()) != EOF) {
             if (ch == ' ')
                ++spazi;
             if ( ch == '\t')
                ++tabulazioni;
             if (ch == '\n')
                ++newline;
        }
        printf("Spazi: %d, tabulazioni: %d, newline: %d, EOF: %d", spazi, tabulazioni, newline, ch);
    
        return 0;
    }
    
    se inserisco l'EOF all'interno di una serie di una serie di caratteri prima di aver premuto invio, il ciclo while non si arresta subito, ma devo inserire di nuovo l'EOF. Ad esempio (dove EOF è ^Z) se digito ciao^Z il cursore si sposta alla linea successiva ma il controllo è ancora all'interno del ciclo. Non dovrebbe invece uscire dal ciclo visto che l'ultimo carattere letto dalla getchar è l'EOF?

    PS: la versione sintetica non dovrebbe essere while (((ch = getchar()) != '\n') && (ch != EOF)); ? perchè altrimenti in ch ti ritrovi vero o falso credo.
  • Re: KR ciclo while con funzione getchar (e domanda sul main)

    Usare la getchar() su stdin, lo stream di input relativo alla console di default, implica un problema di buffering poiché tale stream è per default buffered, anche se tale stato si può facilmente alterare con una chiamata di sistema (ovviamente ben diversa tra DOS, Windows, Linux, OS/2, MacOS, OpenBSD...), ma al momento puoi ignorare la cosa.

    Prova invece a dare in pasto al tuo programmino un file da disco: anche lo stesso sorgente C del programma. Osserverai il comportamento desiderato. La sintassi da command line ovviamente è la seguente, usando il redirect della shell:
    
    miofile<miofile.c
    

    La sintassi della versione sintetica della clearKeyboardBuffer() proposta è corretta e convalidata.
  • Re: KR ciclo while con funzione getchar (e domanda sul main)

    M.A.W. 1968 ha scritto:


    Usare la getchar() su stdin, lo stream di input relativo alla console di default, implica un problema di buffering poiché tale stream è per default buffered, anche se tale stato si può facilmente alterare con una chiamata di sistema (ovviamente ben diversa tra DOS, Windows, Linux, OS/2, MacOS, OpenBSD...), ma al momento puoi ignorare la cosa.
    Per ora sarò felice ignorare questa parte che non c'ho capito niente ci sarà tempo.
    La sintassi della versione sintetica della clearKeyboardBuffer() proposta è corretta e convalidata.
    Non mi stavo nemmeno sognando di metterlo in dubbio ed ero sicuro non fosse un errore (non vedo i miei figuriamoci...), era per capire, passando da una forma all'altra, perchè non servono quelle parentesi? Più che altro il dubbio era di non aver compreso il significato di quel costrutto.

    PS: Capisco di stare abusando della tua disponibilità, ma hai presente quando i prof dicono di fare domande, anche se sceme per capire meglio? Io non ne ho mai fatte perchè vedevo che poi alla prima domanda scema non perdevano occasione di umiliare chi l'aveva fatta L'anonimato del forum invece mi spinge a fare tutte le domande sceme che mi passano per la testa e poi ormai ti ho eletto a mio maestro senza il tuo consenso
  • Re: KR ciclo while con funzione getchar (e domanda sul main)

    meganerd_II ha scritto:


    ...passando da una forma all'altra, perchè non servono quelle parentesi? Più che altro il dubbio era di non aver compreso il significato di quel costrutto.
    Si tratta di sfruttare la precedenza degli operatori. Ma didatticamente (e non solo) è sempre meglio abbondare con le parentesi, almeno fino a quando non si rischia di sconfinare nell'eccesso opposto, ossia rendere illeggibile l'espressione.

    meganerd_II ha scritto:


    PS: Capisco di stare abusando della tua disponibilità, ma hai presente quando i prof dicono di fare domande, anche se sceme per capire meglio? Io non ne ho mai fatte perchè vedevo che poi alla prima domanda scema non perdevano occasione di umiliare chi l'aveva fatta L'anonimato del forum invece mi spinge a fare tutte le domande sceme che mi passano per la testa e poi ormai ti ho eletto a mio maestro senza il tuo consenso
    Non c'è alcun problema, ti invierò la parcella in privato assieme al codice per il versamento...
    L'unico vero problema è che ho pochissimo tempo, questa è una parentesi insolita. Ma non manca chi saprà rispondere ai tuoi interrogativi sul forum.
Devi accedere o registrarti per scrivere nel forum
7 risposte