elhacker.net cabecera Bienvenido(a), Visitante. Por favor Ingresar o Registrarse
¿Perdiste tu email de activación?.

 

 


Tema destacado: Sigue las noticias más importantes de seguridad informática en el Twitter! de elhacker.NET


  Mostrar Temas
Páginas: [1]
1  Programación / Programación C/C++ / El bendito stdin... en: 24 Noviembre 2014, 00:28 am
Hace unos días publiqué un tema donde al parecer se limpiaba el estándar de entrada de forma correcta y así evitábamos comportamientos indeseados en una lectura desde este, de forma portable (independiente del sistema operativo).

Hasta ahora no he tenido problemas al leer desde este con cualquiera de las siguientes funciones (aunque prefiero la segunda):

Código
  1. size_t leerLinea(char *arr, size_t tam)
  2. {
  3. int c = EOF;
  4. size_t i;
  5.  
  6. if (!tam)
  7. return 0;
  8.  
  9. for (i = 0; i < tam - 1 && (c = getchar()) != EOF && c != '\n'; ++i)
  10. arr[i] = c;
  11.  
  12. arr[i] = '\0';
  13.  
  14. while (c != '\n' && c != EOF)
  15. c = getchar();
  16.  
  17. return i;
  18. }
  19.  
  20. size_t leerLinea(char *arr, size_t tam)
  21. {
  22. int c = (tam == 1) ? EOF : 0;
  23.  
  24. if (fgets(arr, tam, stdin) == NULL)
  25. return 0;
  26.  
  27. while (arr[strlen(arr) - 1] != '\n' && c != '\n' && c != EOF)
  28. c = getchar();
  29.  
  30. if (arr[strlen(arr) - 1] == '\n')
  31. arr[strlen(arr) - 1] = '\0';
  32.  
  33. return strlen(arr);
  34. }

Ambas leen tam - 1 caracteres y descartan todo lo que haya luego de los mismos, más sin embargo, aún tengo una duda... la cual creo que haría inútil las funciones anteriores.

Según he leído en las redes, stdin es un (FILE *) stream que, al parecer, sólo se encuentra en memoria; existe un módulo kernel que lo controla y le da un espacio en esta (independientemente del sistema operativo) para que allí, se almacene la información ingresada detrás de cada salto de línea ('\n').

Bien, si todo esto es cierto, me pregunto... ¿cómo acceder a esa zona de memoria? es decir, cómo obtengo esa dirección... la requiero para jugar un rato con la stdin, y ver si me puedo deshacer de las funciones anteriores, pues si logro acceder a esa zona de memoria, tras cada lectura de entrada (con un simple fgets() sin temor a dejar basura) puedo obtener lo que deseo; lo que no, simplemente lo re-escribo con ceros y así "limpio" de una forma más directa la entrada, o bien... almaceno todo lo que el usuario ha ingresado por la terminal y luego doy formato (mi real propósito)...

Lo que busco es, en síntesis, crear una función que reciba todo lo que se ha ingresado por la terminal sin desechar nada, y para eso... requiero saber la ubicación del búfer de stdin para así, como comenté anteriormente, almacenar todo lo que se ingrese... lo deseo hacer por ocio más que nada, sé que sí es posible, ya que la plataforma .NET lo hace... si declaramos un string, no es necesario definir su tamaño ni tampoco el tamaño de la cadena a leer, la plataforma .NET re-asigna memoria hasta lograr almacenar lo que el usuario ha ingresado, en caso de no haber memoria suficiente, lanza una excepción...

Bien, es eso a lo que quisiera llegar... porque sinceramente da pena el manejo de cadenas en C (aunque en C++ se ha mejorado bastante, no es tan eficiente como la plataforma .NET)...

Ya sé que no tiene mucho sentido hacer algo semejante, pues si al caso vamos, si no estamos escribiendo un editor de textos ni nada por el estilo, no es necesario un manejo dinámico de cadenas en C, pues simplemente se usa una de las funciones anteriores para obtener lo que el programa requiere, no lo que el usuario demande... pero mi curiosidad es más grande y como .NET parte de C++ (para C#), y C++ de C, supongo es lógicamente posible. (No quiero reinventar la rueda, simplemente quiero curiosear).

Cualquier información/sugerencia/crítica será bien recibida.

EDITO:

Tal parece que... leyendo un poco sobre punteros (aún no llego a dicho capítulo en el libro de K&R), logré escribir la siguiente función, la cual lee toda la entrada de teclado hasta el salto de línea, y reasigna memoria según necesite cada vez que se llenen 4kB de esta (tengo entendido que Windows y Linux dividen la memoria virtual en páginas de 4kB), es lo que estaba buscando... así pues, me evito problemas con dejar basura en la entrada y obtengo todo lo que el usuario ha ingresado, ya luego doy formato a esa entrada...

Código
  1. char* leerLinea()
  2. {
  3. int c;
  4. size_t p4kB = 4096, i = 0;
  5. void *newPtr = NULL;
  6. char *ptrString = malloc(p4kB * sizeof (char));
  7.  
  8. while (ptrString != NULL && (c = getchar()) != '\n' && c != EOF)
  9. {
  10. if (i == p4kB * sizeof (char))
  11. {
  12. p4kB += 4096;
  13. if ((newPtr = realloc(ptrString, p4kB * sizeof (char))) != NULL)
  14. ptrString = (char*) newPtr;
  15. else
  16. {
  17. free(ptrString);
  18. return NULL;
  19. }
  20. }
  21. ptrString[i++] = c;
  22. }
  23.  
  24. if (ptrString != NULL)
  25. {
  26. ptrString[i] = '\0';
  27. ptrString = realloc(ptrString, strlen(ptrString) + 1);
  28. }
  29. else return NULL;
  30.  
  31. return ptrString;
  32. }

Como se puede observar, la función devuelve un puntero donde se ubica la lectura de la entrada del usuario hasta encontrar EOF o un salto de línea, el cual es reemplazado con un carácter nulo (fin de cadena). En caso de no poder reasignar memoria, libera la utilizada hasta el momento y retorna un puntero nulo. Finalmente si logra leer todos los caracteres y sobra memoria de la reservada, reasigna memoria para liberar todo el espacio sobrante.

Agradecería usuarios más experimentados me dieran sugerencias para modificar el código, no veo más formas de optimizarlo ni hacerlo más seguro... Ya comprobé que sí es POSIX. Y a mis exigencias, cumple con lo que necesito... espero que a alguien le sirva, pero si aún hay modificaciones que se puedan aplicar para mejorar el código, no duden en hacérmelas saber.
2  Programación / Programación C/C++ / [C] Limpiando la stdin correctamente... en: 12 Noviembre 2014, 04:12 am
Hola, he creado una función personalizada de la función getline() que aparece en el libro de K&R (en su primer capítulo)... quisiera me comentaran su comportamiento bajo sistemas Un*x:

Código
  1. int getLine(char buffer[], int max)
  2. {
  3. int c = EOF, i;
  4.  
  5. if (!max)
  6. return 0;
  7.  
  8. for (i = 0; i < max - 1 && (c = getchar()) != EOF && c != '\n'; ++i)
  9. buffer[i] = c;
  10.  
  11. buffer[i] = '\0';
  12.  
  13. while (c != '\n' && c != EOF)
  14. c = getchar();
  15.  
  16. return (i);
  17. }

Para quien no la comprenda, aunque es bastante simple, simplemente se leen hasta max - 1 caracteres y se asignan a buffer[], la diferencia con fgets() es que dicha función nos puede dejar basura en la stdin si el usuario introduce una cadena más larga de lo esperado y así, al momento de intentar leer con cualquier otra función desde la stdin, vamos a leer los caracteres que sobraron de la lectura anterior.

Lógicamente se pueden limpiar con una macro:

Código
  1. #define stdinClr() int c; while ((c = getchar()) != '\n' && c != EOF);

Tal y como lo hago en getLine(), pero si usáramos siempre fgets() + stdinClr(), en caso que el usuario no ingresara más caracteres de lo esperado, fgets() realizaría su trabajo correctamente y el programa esperaría la entrada de un salto de línea o fin de archivo... lo cual es un comportamiento indeseado.

Por lo que la función getLine() considero es la más apropiada para obtener una cadena de n - 1 caracteres y así trabajar con la misma de forma más segura... con la stdin, claramente.

Ergo, quisiera saber si funciona en otros sistemas fuera de la familia NT. Si alguien tiene la amabilidad... lo que deseo es deshacerme de fgets() + stdClr() para con la stdin y hacerlo de forma portable.
Páginas: [1]
WAP2 - Aviso Legal - Powered by SMF 1.1.21 | SMF © 2006-2008, Simple Machines