2016-07-18 135 views
8

La dichiarazione dello sguardo fgets funzione come questa:Come usare fgets() per evitare di trasmettere il suo secondo argomento che è di tipo int?

char *fgets(char *str, int n, FILE *stream); 

Ciò significa che il secondo argomento dovrebbe essere un int.

Qual è il modo corretto per evitare questa trasmissione nel seguente programma?

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

int main(void) { 
    const char *buffer = "Michi"; 
    size_t len = strlen(buffer) + 1; 
    char arr[len]; 

    printf("Type your Input:> "); 
    if (fgets(arr, (int)len, stdin) == NULL) { 
     printf("Error, fgets\n"); 
     exit(1); 
    } else { 
     printf("Arr = %s\n", arr); 
    } 
} 

Qui ho usato (int)len che sembra bene, ma cosa succede se buffer memorizza una stringa molto lunga?

consente di dire:

const char *buffer = "Very long long ..."; /* where length is beyond the range of the `int` type */ 

ho già dichiarato la lunghezza del tipo size_t che va bene qui, ma se io passo a fgets non è OK a causa di:

conversion to ‘int’ from ‘size_t {aka long unsigned int}’ may alter its value 

e se lo trasmetto a int non si adatta perché la dimensione dello int è inferiore alla dimensione di length e alcune informazioni andranno perse.

forse mi manca qualcosa qui ...

Qualsiasi modo come dovrei evitare questa situazione?

+1

Solo dicendo, in questo caso, sei sicuro 'char arr [len] ; 'funzionerà correttamente? È in pila con gcc, comunque .. –

+1

@SouravGhosh Potrei usare malloc qui, ma la domanda non è su quello – Michi

+0

Forse http://stackoverflow.com/questions/8317295/convert-unsigned-int-to-signed-int -C è utile per te. – Sergio

risposta

5
#include <stdio.h> 
char *fgets(char * restrict s, int n, FILE * restrict stream); 

Con fgets(), il buffer di input s[INT_MAX] e oltre non può essere utilizzato.


codice di OP size_t len potrebbe evitare la int cast di len convertendosi in stringa e riconversione una int, che è solo uno spreco. Un cast è la cosa giusta da fare.

Piuttosto che litterare codice con un (int), ridurre/controllare il suo utilizzo e avvolgere il cast in una funzione di supporto limitante.

int fgets_len(size_t len) { 
    return (len < INT_MAX) ? (int) len : INT_MAX; 
} 


size_t len = something_big; 
char *arr = malloc(len); 

... 
if (fgets(arr, fgets_len(len), stdin) == NULL){ 
    printf("Error, Fgets\n"); 
    exit(1); 
}else{ 
    printf("Arr = '%s'\n", arr); 
} 

Se il codice ha bisogno veramente di leggere lunghe linee, considerare ssize_t getline(char **lineptr, size_t *n, FILE *stream); come definito here. Si noti che questa è una funzione di libreria C non standard, tuttavia il suo codice sorgente è facilmente disponibile.


Per quanto riguarda l'uso di pedanti fgets(), ci sono almeno 2 motivi per fgets() per tornare NULL: End-of-file e l'errore di input. Consideriamo ora il caso angolo utilizzando il codice di OP

size_t len = strlen("") + 1; 
char arr[len]; 
if (fgets(arr, (int)len, stdin) == NULL){ 

e casi patologici come

if (fgets(arr, 0, stdin) == NULL){ 
if (fgets(arr, -1, stdin) == NULL){ 

Entrambi sono discussi Is fgets() returning NULL with a short bufffer compliant?

+0

* Con 'fgets()', non è possibile utilizzare il buffer di ingresso 's [INT_MAX]' e oltre. *: Più precisamente, è possibile utilizzare un buffer di qualsiasi dimensione, ma se è più lungo di 'INT_MAX' la sua dimensione non può essere usato direttamente Nota che 's [INT_MAX]' non è un problema. – chqrlie

2

Se si desidera evitare il problema dell'overflow dei numeri interi, è possibile eseguire il controllo del valore e agire di conseguenza. Nota che, dal strlen() restituisce il valore di tipo size_t hai 2 opzioni. Dichiarare una variabile di tipo int e assegnargli il valore restituito di strlen(), che eseguirà una conversione implicita dal tipo size_t a int o eseguirà il cast come si è fatto nel proprio richiamo di funzione.

Ecco una possibile soluzione:

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

int main(void) 
{ 
    const char *buffer = "Michi"; 
    //size_t len = strlen(buffer) + 1; 
    size_t len = 9999999999; 

    if (len > INT_MAX) { 
     fprintf(stderr, "Error, length exceeded desired value.Aborting...\n"); 
     return 1; 
    } 

    char arr[len]; 

    printf("Type your Input:> "); 
    if (fgets(arr, (int)len, stdin) == NULL) { 
     printf("Error, Fgets\n"); 
     exit(1); 
    } 
    else { 
     printf("Arr = %s\n", arr); 
    } 
    return 0; 
} 
+0

So già che 'len> INT_MAX' quindi qui' if (len> INT_MAX) 'il mio programma smetterà di funzionare che è sbagliato. – Michi

+0

@Michi - Se chiami 'fgets' con' n <= 0', restituirà 'NULL' senza nemmeno aspettare l'input, quindi il tuo programma terminerà comunque. Quindi la cosa migliore da fare è controllare 'len' prima di andare. – 4386427

+0

@Michi - Anche il 'size_t' di' int' potrebbe finire con un valore positivo ma privo di significato. L'esempio '0x100000003' potrebbe diventare' 3', il che renderebbe il comportamento del programma strano. Quindi controlla che 'len' non superi' INT_MAX' prima di effettuare la chiamata. – 4386427

0

Draft dice che

Il risultato di convertire un intero in un numero intero breve firmato, o il risultato di conversione di un numero intero senza segno a una firma numero intero di uguale lunghezza, se il valore non può essere rappresentato

è definito dall'implementazione. Di conseguenza, se si desidera convertire in modo sicuro il valore del tipo non firmato in firmato, è necessario assicurarsi che tutti i possibili valori di origine possano essere rappresentati con il tipo di destinazione, firmato.

1

Non mi preoccuperei di collegare la dimensione dell'argomento a fgets() con la dimensione di buffer.

Invece, farei in modo che il buffer utilizzato per la lettura abbia una lunghezza che può essere rappresentata utilizzando un int (ad esempio, non supera INT_MAX). Se vuoi essere veramente portatile, assicurati che la lunghezza del buffer non superi 32767 (lo standard specifica che il valore minimo consentito di INT_MAX è 32767).

Quindi utilizzare il fatto che fgets() leggerà una riga in parti, se la lunghezza della linea supera la lunghezza del buffer.

Ad esempio, supponendo che len superi la lunghezza di qualsiasi riga da leggere da stdin;

char arr[len] = {0}; 
char read_buffer[10]; /* I'm reasonably confident that 10 < 32767 */ 

while (fgets(read_buffer, 10, stdin) != NULL) 
{ 
     size_t read_length = strlen(read_buffer); 
     if (read_length > 0) 
     { 
      if (read_buffer[read_length-1] != `\n`) 
      { 
       strcat(arr, read_buffer); 
      } 
      else 
      { 
       strncat(arr, read_buffer, read_length-1); 

       printf("Arr = %s\n", arr); 

       arr[0] = '\0'; /* clear arr so next line may be read */ 

       /* break here if want to stop reading after the first line */ 

      } 
     } 

Si noti che, se la fine del file non è immediatamente preceduto da un '\n', poi il sopra scarterà il testo dopo l'ultima '\n'.

In precedenza, sostituendo fgets(read_buffer, 10, stdin) con fgets(read_buffer, sizeof read_buffer, stdin) è sicuro, poiché un size_t con valore inferiore o uguale a INT_MAX può sempre tranquillamente essere convertiti int. Quindi, se si vuole stare zitto un compilatore da emettere segnalazioni, si può lanciare in modo sicuro cioè fgets(read_buffer, (int)(sizeof read_buffer), stdin)