Crash..?

di il
55 risposte

55 Risposte - Pagina 2

  • Re: Crash..?

    E se volessi 33 stringhe cosa accadrebbe?
  • Re: Crash..?

    La domanda è rivolta a me, m2? Se sì, cosa intendi con 33 stringhe? Ti riferisci a LMAX_STRINGHE (valore assegnato: 30) e a quel "+2"? Se è così, non capisco il senso della domanda, perché LMAX_STRINGHE indica la quantità massima di caratteri inseribili nel campo, e il "+2" è motivato dalla necessità di "fare spazio" a '\n\0' che aggiunge fgets() per evitare di "sfondare" il buffer. Cerchi di articolare un po' di più la tua domanda, così capisco?

    P.S. Magari, però, la domanda non era rivolta a me...
  • Re: Crash..?

    Intendeva 33 caratteri.

    La fgets non sfonda e non gli servono i due caratteri in più.

    Ti consiglierei dei puntatori e l'allocazione anche per le stringhe (che è quello che ti voleva suggerire...)
  • Re: Crash..?

    Va be', Oregon, qualche tempo fa mi dicevi di non suggerire soluzioni che potessero mettere in difficoltà chi chiede... l'allocazione dinamica è proprio uno dei punti nei quali chi ha fatto la sua domanda ha toppato. Lui stesso aveva impostato la struttura dati con degli array di char di lunghezza predefinita, non dinamici, per cui ho voluto seguire il più da vicino possibile la sua "traccia".

    Comunque, che fgets() non "sfonda", nel senso che non mette nel buffer cose che vadano oltre alla capacità segnalata è vero, però con il terminatore finisci per avere nella stringa un carattere "utile" in meno rispetto a tale capacità (in effetti basta "LMAX_STRINGHE+1", non "+2"; hai ragione se dici che non serve prevedere spazio per '\n'). La funzione ricevi_stringa() l'ho realizzata così:
    int ricevi_stringa( const char *msg, char *buff, int dim_buff ) {
        if( NULL != buff ) {
            if( NULL != msg ) printf( msg );
    
            if( fgets(buff,dim_buff,stdin) ) {
                size_t l = strlen( buff );
                if( '\n' == buff[l-1] )
                    buff[--l] = '\0';
                else while( '\n' != getchar() );
                return 1; // tutto bene
            } else return errore( "errore in fgets()" );
        } else return errore( kStrNullPtr );
    }
    Se ho (per dire) LMAX_STRINGHE impostato su 30 e passo LMAX_STRINGHE alla funzione, le stringhe "utili" che posso ottenere hanno al massimo 29 caratteri (il trentesimo se ne va per '\0'). L'intenzione, invece, era dare per inteso che voglio LMAX_STRINGHE caratteri utili, dal che il "+1" -- passando 31 come dimensione del buffer, posso ottenere stringhe lunghe fino a 30 caratteri "utili", che è quel che volevo.

    Fila?
  • Re: Crash..?

    Sostituendo il "+2" col "+1" diventa...
    #define LMAX_STRINGHE   31
    
    const char *kStrNullPtr = "puntatore nullo";
    const char *kStrNoMem   = "memoria non allocata";
    const char *kStrNoFgets = "errore in fgets()";
    
    typedef struct Car{
        char marca[LMAX_STRINGHE+1];
        char modello[LMAX_STRINGHE+1];
        int  vendute;
    } Car;
    
    int ricevi_intero( const char *msg, int *n );
    int ricevi_auto( Car *c, int iCar );
    int mostra_auto( Car *c, int iCar );
    int conferma_inserimento_auto( Car *c, int iCar );
    int errore( const char *msg );
    void attendi_invio( void );
    
    int main() {
        Car *ac = NULL; // ac: array di Car
        int i, e, qc;   // qc: quantita' di Car nell'array ac
    
        if( !ricevi_intero("quanti elementi vuoi inserire? ",&qc) )
            return 0; // errore gia' segnalato
    
        ac = calloc( qc, sizeof(*ac) );
        if( NULL == ac ) return errore( kStrNoMem );
        // da qui in poi occorre assicurarsi di rilasciare
        // con free() la memoria allocata quando non serve piu'
    
        for( e=0, i=0; i<qc; e=0, i++ ) { // e: esito
            if( ricevi_auto(ac,i) ) {
                e = conferma_inserimento_auto( ac, i );
    
                // e puo' essere: 0 (accettato), -1 (rifiutato), 1 (errore)
                if( e <= 0 ) // se e ==-1: ripetizione della richiesta d'inserimento
                    i += e;  // se e == 0: prosegue con l'inserimento successivo
                else break;  // se e == 1: errore (gia' segnalato)
            } else break;
        }
    
        if( i == qc ) {
            printf( "\necco i dati inseriti:\n" );
    
            for( i=0; i<qc; i++ )
                if( !mostra_auto(ac,i) )
                    break;
    
            attendi_invio();
        }
    
        free( ac ); // libera la memoria allocata alla riga 38 di questo codice
        return 0;
    }
    
    int ricevi_stringa( const char *msg, char *buff, int dim_buff ) {
        if( NULL != buff ) {
            if( NULL != msg ) printf( msg );
    
            if( fgets(buff,dim_buff,stdin) ) {
                size_t l = strlen( buff );
                if( '\n' == buff[l-1] )
                    buff[--l] = '\0';
                else while( '\n' != getchar() );
                return 1; // tutto bene
            } else return errore( kStrNoFgets );
        } else return errore( kStrNullPtr );
    }
  • Re: Crash..?

    A) non sto dicendo di suggerire l'uso dell'allocazione dinamica, confermo quello che ti ho sempre detto, ti stavo solo chiarendo quello ghe intendeva +m+

    b) non ho detto nulla sull'effettiva lunghezza utile per la fgets ma solo che non sfondava come indicavi tu
  • Re: Crash..?

    Capito. Sì, "sfondare" ha un significato diverso, tipo "scrivere oltre il limite indicato", e non era quel che volevo dire. Con la mia ricevi_stringa() non c'è lo sfondamento (che è comunque impossibile, come hai rilevato) e c'è il vantaggio di togliere dallo stream tutti gli eventuali caratteri in eccesso. Inoltre, la stringa viene restituita senza quel (a volte) fastidioso '\n' in coda, dando un input "ripulito".
  • Re: Crash..?

    ... non fila... basta quella strlen() per rendere superfluo leggere il resto.
  • Re: Crash..?

    Perché? Cosa c'è che non va in strlen()? In che altro modo posso sapere dove finisce la stringa immessa? Con un ciclo interno alla funzione? Ero solito farlo, poi mi è stato detto (non da te, non qui) "cos'è, usare strlen() ti fa schifo?"... Dai, non essere sempre ermetico, mi sembri Bartezzaghi!

    P.S. Casomai... https://it.wikipedia.org/wiki/Piero_Bartezzagh
  • Re: Crash..?

    AldoBaldo ha scritto:


    Perché? Cosa c'è che non va in strlen()? In che altro modo posso sapere dove finisce la stringa immessa? Con un ciclo interno alla funzione? Ero solito farlo, poi mi è stato detto (non da te, non qui) "cos'è, usare strlen() ti fa schifo?"... Dai, non essere sempre ermetico, mi sembri Bartezzaghi!

    P.S. Casomai... https://it.wikipedia.org/wiki/Piero_Bartezzagh
    Sì, strlen fa schifo.
    Quasi come gli interi signed nei parametri.
    E in generale tutto quello che manipola stringhe C e non sia (almeno) _s.

    Dovrei tirare fuori il pippone del dipartimento dei tonti USA per i programmini C ma sono alle prese con una bella rogna.
  • Re: Crash..?

    Ancora, in occasioni precedenti mi è stato rimproverato di rispondere con "situazioni" inadatte al livello delle questioni proposte da chi chiede. Tempo fa, mi venne anche rimproverato in questo forum di avere usato il tipo size_t per indicare le dimensioni di un buffer, osservando che size_t non è un tipo "comune" e può confondere le idee a chi non ne ha mai sentito parlare. Ora tiri fuori riferimenti alla sicurezza, che credo vadano un po' più in là di un esercizio proposto da chi ha problemi ad usare malloc().

    Le osservazioni che fai, per quanto senz'altro giuste in altri ambiti (sei tu l'esperto), direi che cozzano con queste premesse.

    In dialetto sintetizzerei con un "bütev d’acordi’n’ter vi öuter".
  • Re: Crash..?

    Non conosco quelle lingue straniere, ma il C decisamente bene.
    Difficilmente ti rimprovererò, giacchè non è il mio compito.
    Mi rendo conto che siamo a livello basissimo, tuttavia ho notato che hai messo dei controlli nella funzione.
    int ricevi_stringa( const char *msg, char *buff, int dim_buff ) {
    Cominciamo col dire che nomi variabili così ridicoli confondono solo (e questo è un insegnamento anche e soprattutto per i dilettanti).
    Nessuno saprà mai cos'è msg, buff, e dim_buff.

    Quindi il dilettante potrebbe orientarsi su qualcosa del genere i_messaggio, i_buffer, i_dimensionebuffer.
    dove la i_ specifica che si tratta di un parametro di INPUT (cosa non così scontata, in realtà in C è più evidente rispetto a C++, comunque pazienza).

    Se decidi di mettere dentro dei controlli, nella funzione, in tal modo complicando la vita al dilettante, aggiungerei anche che il terzo parametro è un INTERO, e quindi può essere NEGATIVO, il che significa poi POSITIVO grande

    Cosa succede se passo un -10 come terzo parametro?

    strlen è una delle peggiori funzioni note all'uomo, al pari di gets
    https://www.us-cert.gov/bsi/articles/knowledge/coding-practices/fgets-and-gets_s

    Versioni più "evolute" del linguaggio hanno introdotto le funzioni _s, proprio al fine di tentare di limitare le voragini dei buffer overflow.

    Questo frammentino di codice, a uno come me, fa venire i sudori freddi
    
                if( '\n' == buff[l-1] )
                    buff[--l] = '\0';
    Riassunto: (ed ecco il rimprovero).
    O fai un programma per didattica, allora togli via tutta quella spazzatura che hai messo, fa solo confusione.
    O fai un programma del livello di un programmatore junior.
    O fai come ti pare
    (questo è un IF con tre risultati)
  • Re: Crash..?

    Guarda, ribadendo che senz'altro ne sai ben più di me (ma mica poco, nè!)

    1) "msg" è un'ovvia contrazione di "messaggio"; perfino Microsoft usa MSG per denominare un tipo dati che rappresenta la struttura usata per scambiare "messaggi" in varie situazione.
    2) "buff" è un'ovvia contrazione di "buffer"; in mille esempi di codice, nei testi che son solito consultare, si usa "buff" con quel significato, per cui IPOTIZZO sia abbastanza comune da poter essere considerato chiaro.
    3) "dim_buff" è un'ovvia contrazione di "dimensione buffer"; anche questo è un identificatore comune, a volte espresso come dimBuff (più spesso ho trovati buffSize, che è la stessa cosa).

    Comunque sia, in una funzione di undici righe è facile capire il senso di tre parametri in croce, anche senza scrivere dei nomi chilometrici. Nessuno vieta di aggiungere un commento (di solito lo faccio), tipo...
    /*==============================================================================
    ricevi_stringa()
    
    La funzione fa questo e quello, bla, bla bla...
    
    Parametri:
        msg:        una stringa C da usare come messaggio da visualizzare per
                    esplicitare l'immissione richiesta
        buff:       un buffer nel quale viene collocata l'immissione
        dim_buff:   la capacita' massima del buffer puntato da buff
        
    Valore di ritorno:
        0           l'immissione e' fallita; il contenuto di buff non e' affidabile
        1           l'immissione e' riuscita; la stringa immessa si trova in buff
    ==============================================================================*/
    4) Chi passasse -10 in un parametro che rappresenta la dimensione di un buffer sarebbe un tipo un po' distratto; chi è distratto può passare un valore negativo anche se il tipo previsto è unsigned int, perché avviene una conversione automatica senza che il compilatore batta un colpo (almeno, non il compilatore che uso io).

    5) Ho provato impostando il compilatore con ISO C90, ISO C99 e ISO C11: in nessuno dei tre standard mi viene accettata strlen_s().

    6) if( '\n' == buff[l-1] ) buff[--l] = '\0'; controlla se l'ultimo carattere della stringa è '\n' e, se è così, lo elimina sostituendolo con '\0', con l'effetto (subito registrato in l) di ridurre di un carattere la lunghezza della stringa. Cosa c'è che non va?

    Il discorso non è "fai quel che ti pare", è ovvio che posso fare quel che mi pare. Però in una discussione ci si aspetta di leggere delle argomentazioni. Finché scrivi cose tipo "basta quella strlen() per rendere superfluo leggere il resto" non è che dai molti spunti, senza contare che l'impressione che si ricava leggendo è quella di un tono piuttosto sprezzante.
  • Re: Crash..?

    AldoBaldo ha scritto:


    Guarda, ribadendo che senz'altro ne sai ben più di me (ma mica poco, nè!)

    1) "msg" è un'ovvia contrazione di "messaggio"...2) "buff" è un'ovvia contrazione di "buffer"...
    Tutto è ovvio.
    Tranne quando non lo è.
    Cos'è il messaggio? Una stringa? Un payload UDP? Un pezzo di un json?
    Buff cosa fa? Cosa c'è dentro? Un file ? Cibo per gatti?
    Comunque sia, in una funzione di undici righe è facile capire il senso di tre parametri in croce...
    Vabbè, si vede che per te è facile.
    Per me non così tanto.
    I commenti che hai messo sono del tutto inutili, se i parametri sono autoesplicativi.
    In certi casi, ovviamente, non lo sono affatto.
    Ma se guardi quanto hai scritto ti renderai conto che fai un papiro per chiarire quello che dovrebbe già esserlo.
    4) Chi passasse -10 in un parametro che rappresenta la dimensione di un buffer sarebbe un tipo un po' distratto...
    Bhè altrettanto sarebbe "distratto" chi passasse un NULL. O no?
    5) Ho provato impostando il compilatore con ISO C90, ISO C99 e ISO C11: in nessuno dei tre standard mi viene accettata strlen_s().
    E come mai?
    6) if( '\n' == buff[l-1] ) buff[--l] = '\0'; controlla se l'ultimo carattere della stringa è '\n' e, se è così, lo elimina sostituendolo con '\0', con l'effetto (subito registrato in l) di ridurre di un carattere la lunghezza della stringa. Cosa c'è che non va?
    Davvero? Non lo avrei mai intuito!
    Operazioni di modifica delle stringhe, così, a casaccio, basate su un indice (l) è un ottimo modo per aprire voragini di sicurezza di ogni genere (oltre che a più "normali" bug).
    Il discorso non è "fai quel che ti pare", è ovvio che posso fare quel che mi pare. Però in una discussione ci si aspetta di leggere delle argomentazioni. Finché scrivi cose tipo "basta quella strlen() per rendere superfluo leggere il resto" non è che dai molti spunti, senza contare che l'impressione che si ricava leggendo è quella di un tono piuttosto sprezzante.
    Il tono è certamente sprezzante, nel senso che è grosso modo come alle prime lezioni d'inglese.
    La penna è sul tavolo, la penna è sotto al tavolo.

    Dovrei aprire un corsi di 3 mesi su come si scrivono programmi C appena a livello "la penna è sotto il tavolo".
    E' palese, o almeno lo è per me, che strlen cerca di fare qualcosa senza sapere cosa riuscirà a fare.
    Se, per qualsiasi motivo, non trova uno "zero" può succedere di tutto e di più (compreso, ma dipende dai casi, ritornare perfino un valore negativo, o meglio col bit negativo settato).

    Versione breve: ogni volta che si vede strlen (e neppure strnlen) è già evidente che si tratta della penna sul tavolo o sotto il tavolo.

    Come un pò di sprezzo. Con l'accento, non l'apostrofo.

    PS ovviamente non apro il pippone su cosa sia una STRINGA, oggi, nel mondo UTF-8, UTF-16 e UTF-32. Quello è a livello "una patata, due patate, tre patate..." (sì, ai miei tempi le filastrocche d'inglese prevedevano di contare... le patate )
  • Re: Crash..?

    Potresti riscrivere la libreria standard del C...
Devi accedere o registrarti per scrivere nel forum
55 risposte