2013-06-26 14 views
6

ho:Lunghezza massima stringa usando scanf -> ANSI C

#define MAX_STR_LEN 100 

e voglio mettere in scanf modello in modo da poter controllare la lunghezza della stringa:

scanf("%100[^\n]s",sometext) 

ho provato:

scanf("%MAX_STR_LEN[^\n]s",sometext) 
scanf("%"MAX_STR_LEN"[^\n]s",sometext) 
scanf("%",MAX_STR_LEN,"[^\n]s",sometext) 

E non ha funzionato. Voglio solo evitare l'overflow del buffer poiché "sometext" è allocato con malloc(MAX_STR_LEN) ...

Qualche idea?

+0

Discusso [qui] (http://stackoverflow.com/questions/9457325/how-to-use-sscanf-correctly-and-safely). –

+0

Come si può usare 'MAX_STR_LEN' in una stringa di formato? – amulous

+0

@amolto, poiché è un rigoroso ANSI C, in realtà non ho accesso alla variabile stringa reale. Devo fare: char * somestring e poi malloc ... – tomdavies

risposta

10

non ero felice con qualsiasi di queste soluzioni, così ho ricercato ulteriormente, e ha scoperto GNU GCC macro stringification

che può essere usato come:

#define XSTR(A) STR(A) 
#define STR(A) #A 
#define MAX_STR_LEN 100 
scanf("%"XSTR(MAX_STR_LEN)"[^\n]s", sometext) 

Forse VS2010 offre qualcosa di simile?

+5

Questo è nello standard ANSI C - dovrebbe * davvero * funzionare con VS 2010. (Date le idiosincrasie di VS, però, non sono sicuro.) – michaelb958

+2

Questa è la risposta corretta. A tutti quelli che dicono "non usare scanf()": questo dovrebbe essere sufficiente per evitare buffer overflow come descritto da OP. – jimis

+0

Sfortunatamente, questo rompe la maggior parte dei controlli di stringa di formato IDE e di compilazione. –

7

voglio solo evitare di buffer overflow

Allora non usare scanf(). Affatto.

Se si esegue la scansione di righe di testo, non specificare #define MAX_STR. È possibile haz LINE_MAX a <limits.h> (se si prendono di mira sistemi compatibili POSIX):

char buf[LINE_MAX]; 
fgets(buf, sizeof(buf), stdin); 

dovrebbe fare il trucco.

-3

Come su

scanf("%.*[^\n]s", MAX_STR_LEN, sometext) 
+0

Per favore, per favore, *** No! *** –

+3

Raccomanda di rileggere l'uso di '*' in 'scanf()'. Sono certo che vorresti cambiare il tuo post. Nell'uso printf(), '*' legge un numero intero per la dimensione, ma non in 'scanf()'. '*' significa "Un asterisco iniziale facoltativo indica che i dati devono essere letti dallo stream ma ignorati". Una funzione molto diversa. – chux

0

Consiglia l'approccio fgets(buffer, sizeof(buffer), stdin).

Se si ancora si desidera utilizzare scanf() è possibile creare il suo formato in fase di esecuzione.

#define MAX_STR_LEN 100 
char format[2 + sizeof(size_t)*3 + 4 + 1]; // Ugly magiC# 
sprintf(format, " %%%zu[^\n]", (size_t) MAX_STR_LEN); 
scanf(format, sometext); 

o ridefinire MAX_STR_LEN essere una stringa

#define MAX_STR_LEN "100" 
scanf(" %" MAX_STR_LEN "[^\n]", sometext); 

Ancora consiglia l'fgets().
Nota fgets() inserisce spazi iniziali e il trailing \n nel buffer mentre " %[^\n]" no.
BTW: il trailing s nei tuoi formati non è probabile che faccia quello che pensi.

4

Come quasi tutti dicono, è meglio utilizzare fgets(..., stdin) per gestire questo problema.

Nel seguente link, mi hanno suggerito una tecnica sicura e corretta che consentono di sostituire scanf() con un metodo più sicuro, per mezzo di un solido macro:

A macro that safely replaces scanf()

La macro che ho proposto (lavorando con compatibili C99 compilatori) è safe_scanf(), come mostrato nella seguente programma:

#include <stdio.h> 
#define safe_scanf(fmt, maxb, ...) { \ 
    char buffer[maxb+1] = { [maxb - 1] = '\0' }; \ 
    fgets(buffer, maxb+1, stdin); \ 
    if ((buffer[maxb - 1] != '\0') && (buffer[maxb - 1] != '\n')) \ 
     while(getchar() != '\n') \ 
      ; \ 
    sscanf(buffer, fmt, __VA_ARGS__); \ 
    } 

#define MAXBUFF 20  

int main(void) { 
    int x; float f;  
    safe_scanf("%d %g", MAXBUFF+1, &x, &f); 
    printf("Your input was: x == %d\t\t f == %g", x, f); 
    return 0; 
} 

si dovrà sintonizzare il valore di MAXBUFF base alle vostre esigenze ...
Anche se la macro safe_scanf() è piuttosto solido,
ci sono alcuni debolezza utilizzando l'approccio macro:
mancante tipo-controllo per i parametri mancano i valori di "ritorno" (che difficilmente differiscono dalla funzione "vera" scanf(), che restituisce un int, con informazioni preziose per il controllo degli errori), e così via.
Tutto ciò che i problemi hanno una soluzione, ma è parte di un altro argomento ...

Forse, la soluzione più precisa è quella di definire una funzione my_scanf() con numero variabile di parametri, invocando la biblioteca stdarg.h, congiunta ad una combinazione di fgets() e vsscanf(). Qui si ha il codice:

#include <stdio.h> 
#include <stdarg.h> 

int my_scanf(const char* fmt, const unsigned int maxbuff, ...) { 
    va_list ptr; 
    int ret; 

    if (maxbuff <= 0) 
     return EOF; /* Bad size for buffer[] */ 

    char buffer[maxbuff+1]; 
    buffer[maxbuff-1] = '\0'; /* Quick buffer cleaning... */ 

    if (fgets(buffer, maxbuff+1, stdin) == NULL) 
     return EOF; /* Error detected */ 
    else { 
     if ((buffer[maxbuff-1] != '\n') && (buffer[maxbuff-1] != '\0')) 
      /* Condition logically equivalent to: 
        fgets() has not reached an '\n' 
      */ 
      while (getchar() != '\n') 
       ; /* "Flushing" stdin... */ 

     va_start(ptr, maxbuff); 
     ret = vsscanf(buffer, fmt, ptr); 
     va_end(ptr); 
     return ret; 
    }  
} 

#define MAXBUFF 20 
int main(void) { 
    int x; 
    float z; 
    int scanf_ret = my_scanf("%d %g", MAXBUFF, &x, &z); 
    printf("\nTest:\n x == %d\n z == %g\n scanfret == %d", x, z, scanf_ret); 
    getchar(); 
    return 0; 
} 

La funzione my_scanf() ha il prototipo

int my_scanf(const char* fmt, const int maxbuff, ...); 

Si accetta una stringa di formato fmt che si comporta nello stesso modo di qualsiasi altra scanf() -come fa.
Il 2 ° parametro è il numero massimo di caratteri che verrà effettivamente accettato dallo standard input (tastiera).
Il valore restituito è int int, che è EOF se maxbuff non ha senso, o bene si è verificato un errore di input. Se viene restituito un valore non negativo, è lo stesso che verrebbe restituito dalle funzioni standard sscanf() o vsscanf().

All'interno della funzione, maxbuff viene incrementato in 1, perché fgets() crea spazio per un carattere '\ 0' aggiuntivo.
I valori non positivi di maxbuff vengono immediatamente eliminati.
fgets() leggerà una stringa viene letta da stdin (tastiera) con spazio per al massimo maxbuff caratteri, incluso '\ n'.
Se l'utente ha immesso una stringa molto lunga, verrà troncato e sarà necessario un meccanismo di "svuotamento" per scartare tutti i caratteri al successivo '\ n' (ENTER). In caso contrario, la prossima lettura della tastiera potrebbe avere caratteri precedenti, non desiderati affatto.
La condizione per "flushing" è che fgets() non ha raggiunto '\ n' dopo aver letto stdin.
Questo è il caso se, e solo se, buffer[maxbuff - 1] non è uguale a "\ 0" né a "\ n".
(controllare!)
Infine, una combinazione appropriata di stdarg.hmacro e la funzione vsscanf() è impiegato per elaborare la lista variabile di parametri.

Problemi correlati