2009-11-29 22 views
5

sé en C puede declarar una cadena y el número de personajes como a continuación,determinar la longitud del de Char cadena en C - si el usuario introduce los contenidos de la cadena

char mystring[50]; 

con '50' es el número de caracteres .

Sin embargo, ¿cuál es el procedimiento correcto si el usuario va a ingresar el contenido de la cadena (a través de scanf ("% s", mystring);)? ¿Lo dejo como,

char mystring[0]; 

dejándolo como '0' ya que no tengo idea de cuántos caracteres ingresará el usuario?

O lo hago,

char mystring[400]; 

dar hasta 400 caracteres para que el usuario de entrada?

Respuesta

6

Ha encontrado el problema exacto con scanf() y% s: ¿qué sucede cuando no sabe cuánta información hay?

Si intenta ejecutar char mystring[0];, su programa compilará muy bien. Pero siempre segfault. Está creando una matriz de tamaño 0, por lo que cuando intente colocar algo en esa matriz, saldrá inmediatamente de los límites de su cadena (ya que no se habrá asignado memoria), que es una segfault.

Por lo tanto, punto 1: siempre debe asignar un tamaño para su cadena. Puedo pensar en muy pocas circunstancias (de acuerdo, ninguna) en las que desee decir char mystring[0] en lugar de char *mystring.

A continuación, cuando utilice scanf, nunca querrá usar el especificador "% s", ya que esto no hará ninguna comprobación de límites en el tamaño de la cadena. por lo que incluso si usted tiene:

char mystring[512]; 
scanf("%s", mystring); 

si el usuario introduce más de 511 caracteres (ya que el 512º es \ 0), has de salir de los límites de la matriz.La manera de remediar esto es:

scanf("%511s", mystring); 

Todo esto es para decir que C no tiene una facilidad para cambiar automáticamente el tamaño de una cadena si hay más de entrada que usted está esperando. Este es el tipo de cosas que tienes que hacer manualmente.

Una forma de resolver esto es usando fgets().

Se podría decir:

while (fgets(mystring, 512, stdin)) 
{ 
    /* process input */ 
} 

A continuación, puede utilizar sscanf() para analizar mystring

Pruebe el código anterior, con una cadena de longitud 5. Después de 4 caracteres han sido leídos, ese código vuelve a repetir para recuperar el resto de la entrada. El "procesamiento" podría incluir código para reasignar una cadena para que tenga un tamaño mayor y luego agregar la última entrada de fgets().

El código anterior no es perfecto: haría que el programa repunte y procese cualquier longitud infinita de cadena, por lo que es posible que desee tener algún límite interno duro (p. Ej., Realice un bucle de hasta 10 veces).

+0

t debe agregarse que% s lee palabras, no cadenas enteras. Porque la cadena de formato de scanf usa espacios y saltos como delimitadores.En este caso, use% c en su lugar (con un ancho de campo) o fgets como mencionó. En el caso de% c con un ancho de campo, recuerde inicializar toda la cadena de almacenamiento intermedio a cero. –

+0

El programa no siempre segfault. De hecho, probablemente no la mayoría de las veces. Su programa probablemente se rompa silenciosamente. ¿No es C adorable? :-) –

2

El usuario siempre podrá ingresar más caracteres, desbordando así su búfer (una fuente común de vulnerabilidades de seguridad). Puede, sin embargo, especifica un "ancho de campo" a scanf, así:

scanf("%50s", mystring); 

En este caso, el tampón debe ser 51 caracteres, para tener en cuenta el campo de 50 caracteres más el terminador nulo. O haga que su memoria intermedia tenga 50 caracteres y diga a scanf 49 que es el ancho.

+0

pero al declarar la cadena, ¿debo especificar '0' o algún número grande? – HollerTrain

+1

Debe especificar al menos 51, en este ejemplo. (La longitud + 1 para el terminador nulo.) – Thanatos

+0

ok. Entonces, ¿se muestra como '0' cuando se declara que la cadena no es la adecuada? Mi problema es que no tengo idea de cuántos usuarios ingresará, pero al mismo tiempo quiero aprender el método correcto ... – HollerTrain

2

Hay una función llamada ggets() que no es parte de la biblioteca C estándar. Es una función bastante simple. Inicializa una matriz char usando malloc(). Luego lee caracteres de stdin uno char a la vez. Realiza un seguimiento de la cantidad de caracteres que se leyeron y expande la matriz de caracteres con realloc() cuando se queda sin espacio.

Está disponible aquí: http://cbfalconer.home.att.net/download/index.htm

que sugeriría leer el código y volver a aplicar a sí mismo.

0

La práctica habitual en C es usar algo como GNU readline o tal vez NetBSD editline, aka libedit. (misma API, diferente implementación y licencia de software.)

Para un programa de tareas simples o, usted podría, en teoría, da un ancho campo para scanf , pero una práctica más normal es fgets() a una matriz de ancho fijo y luego ejecutar sscanf() en eso. De esta forma, usted tiene el control del número de líneas que se leen.

0

Como ejemplo, si el usuario está ingresando su nombre, entonces no siempre es seguro maximizar el tamaño de 'mystring' como 35 caracteres porque algunas personas tienen nombres realmente largos. No desea llegar al caso donde el usuario no puede ingresar la información que está solicitando, en su totalidad. La forma correcta de hacerlo sería tener un búfer temporal con un tamaño muy grande que cubra todas las entradas posibles por parte del usuario. Una vez que el usuario ingresa la información y se almacena en el búfer, transfiere los caracteres desde el búfer a mystring mientras corta todo el espacio adicional al final del búfer. Podrás saber exactamente el tamaño que necesitas para 'mystring' y puedes malloc esa cantidad de espacio y descartar el búfer. De esta forma, no usará una cadena que use más memoria para el resto del programa ... solo usará una cadena con la cantidad de memoria que necesita.

+0

Todavía tendría que hacer algún tipo de comprobación para asegurarse de que lo que el usuario ingresa no sea más grande que el búfer asignado en los casos muy raros o cuando alguien está tratando de explotar su programa. –

1

Este es el código del cbfalconer (http://cbfalconer.home.att.net/download/index.htm) con un par de pequeñas modificaciones y compilado en un archivo:

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

#define INITSIZE 112 /* power of 2 minus 16, helps malloc */ 
#define DELTASIZE (INITSIZE + 16) 

enum {OK = 0, NOMEM}; 

int fggets(char* *ln, FILE *f) 
{ 
    int  cursize, ch, ix; 
    char *buffer, *temp; 

    *ln = NULL; /* default */ 
    if (NULL == (buffer = malloc(INITSIZE))) return NOMEM; 
    cursize = INITSIZE; 

    ix = 0; 
    while ((EOF != (ch = getc(f))) && ('\n' != ch)) { 
     if (ix >= (cursize - 1)) { /* extend buffer */ 
     cursize += DELTASIZE; 
     if (NULL == (temp = realloc(buffer, (size_t)cursize))) { 
      /* ran out of memory, return partial line */ 
      buffer[ix] = '\0'; 
      *ln = buffer; 
      return NOMEM; 
     } 
     buffer = temp; 
     } 
     buffer[ix++] = ch; 
    } 
    if ((EOF == ch) && (0 == ix)) { 
     free(buffer); 
     return EOF; 
    } 

    buffer[ix] = '\0'; 
    if (NULL == (temp = realloc(buffer, (size_t)ix + 1))) { 
     *ln = buffer; /* without reducing it */ 
    } 
    else *ln = temp; 
    return OK; 
} /* fggets */ 
/* End of ggets.c */ 

int main(int argc, char **argv) 
{ 
    FILE *infile; 
    char *line; 
    int cnt; 

    //if (argc == 2) 
     //if ((infile = fopen(argv[1], "r"))) { 
     cnt = 0; 
     while (0 == fggets(&line, stdin)) { 
      fprintf(stderr, "%4d %4d\n", ++cnt, (int)strlen(line)); 
      (void)puts(line); 
      free(line); 
     } 
     return 0; 
     //} 
    //(void)puts("Usage: tggets filetodisplay"); 
    //return EXIT_FAILURE; 
} /* main */ 
/* END file tggets.c */ 

he comprobado y siempre le dará lo que quiere.

+0

Básicamente, para obtener su código original, descomenta los comentarios y reemplaza stdin con infile en la llamada de fggets. –

Cuestiones relacionadas