Foro de elhacker.net

Programación => Programación C/C++ => Mensaje iniciado por: Miky Gonzalez en 10 Octubre 2013, 18:43 pm



Título: [Ayuda-C] Bucle infinito
Publicado por: Miky Gonzalez en 10 Octubre 2013, 18:43 pm
Estaba programando en C un ensamblador para programas de mi máquina virtual (decidí continuar un proyecto). Tengo el siguiente código:

Código
  1. /*! Ejemplo de lector de archivo */
  2.  
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5.  
  6. /*! Declaración de variables locales */
  7. enum {lex_PARI = '(', lex_PARD = ')', lex_LLAI = '{', lex_LLAD = '}'};
  8. unsigned short int pos = 0x00;
  9. unsigned int codigo_volumen;
  10. unsigned char *codigo;
  11. FILE *codigo_archivo;
  12.  
  13. void siguiente_lexema(unsigned char *codigo);
  14.  
  15. void buscar_caracter(char caracter) {
  16. while((caracter != codigo[pos]) && (pos < codigo_volumen)) {
  17. siguiente_lexema(codigo);
  18. pos++;
  19. }
  20. if(pos == codigo_volumen)
  21. printf("Se esperaba '%c'", caracter);
  22. }
  23.  
  24. // modo == 0 (si cadena)
  25. // modo == 1 (si entero)
  26. void identificador(unsigned char *codigo, char modo) {
  27.  
  28. return;
  29. }
  30.  
  31. int saber_letra(char caracter) {
  32. return (((caracter >= 'A') && (caracter <= 'Z')) || ((caracter >= 'a') && (caracter <= 'z')));
  33. }
  34.  
  35. int saber_numero(char caracter) {
  36. return ((caracter >= '0') && (caracter <= '9'));
  37. }
  38.  
  39. void siguiente_lexema(unsigned char *codigo) {
  40. switch(codigo[pos]) {
  41. case lex_PARI:
  42. printf("Encontrado: %c\n", lex_PARI);
  43. buscar_caracter(lex_PARD);
  44. break;
  45. case lex_LLAI:
  46. printf("Encontrado: %c\n", lex_LLAI);
  47. buscar_caracter(lex_LLAD);
  48. break;
  49. default:
  50. if(saber_letra(codigo[pos]))
  51. identificador(codigo, 0);
  52. else if(saber_numero(codigo[pos]))
  53. identificador(codigo, 1);
  54. /*
  55. * TODO: Mostrar error. Ignorar espacios, salto de línea y tabulador
  56. */
  57. else
  58. ;
  59. break;
  60. }
  61.  
  62. return;
  63. }
  64.  
  65. int main(int argc, char **argv) {
  66. if(argc < 2) {
  67. printf("Uso: %s <archivo>\n", argv[0]);
  68. return 0;
  69. }
  70. codigo_archivo = fopen(argv[1], "r");
  71. if(!codigo_archivo) {
  72. printf("[ASM] Error al leer el archivo %s\n", argv[1]);
  73. return 0;
  74. }
  75.  
  76. /* Calcular tamaño (volumen) del código */
  77. fseek(codigo_archivo, 0, SEEK_END);
  78. codigo_volumen = ftell(codigo_archivo);
  79. rewind(codigo_archivo);
  80. if(!codigo_volumen) {
  81. printf("[ASM] No hay instrucciones...\n");
  82. fclose(codigo_archivo);
  83. return 0;
  84. }
  85.  
  86. /* Reservar el espacio necesario para almacenar
  87. * el código en memoria. Si existe, copiarlo. */
  88. codigo = (unsigned char *)malloc(codigo_volumen);
  89. if(!fread(codigo, 1, codigo_volumen, codigo_archivo)) {
  90. printf("[ASM] Error en lectura de archivo...\n");
  91. fclose(codigo_archivo);
  92. return 0;
  93. } else fclose(codigo_archivo);
  94.  
  95. while(pos < codigo_volumen) {
  96. siguiente_lexema(codigo);
  97. pos++;
  98. }
  99.  
  100. return 0;
  101. }
  102.  

El problema está en que dado un archivo:

Código
  1. {
  2. {
  3. {
  4. ( {} )( } } }
  5.  

Entra en un bucle infinito. He revisado muchas veces el código pero no veo ningún error de direcciones, punteros... ¿Alguien puede determinar a causa de que?


Título: Re: [Ayuda-C] Bucle infinito
Publicado por: 0xDani en 10 Octubre 2013, 21:32 pm
Quizá sea porque siguiente_lexema() llama a buscar_caracter(), que a su vez llama a siguiente_lexema()...

Si quieres hacer un ensamblador te recomendaría usar un lenguaje más sencillo como Python y expresiones regulares, si el rendimiento no te importa demasiado.


Título: Re: [Ayuda-C] Bucle infinito
Publicado por: xv0 en 10 Octubre 2013, 21:56 pm
Depuralo asi sabras donde esta el problema.

Tambien podrias hacer funciones como esta te dejo este link, es un deliminador de caracteres pero puedes adaptarlo, mira el ultimo mensaje que deje.

Código:
http://foro.elhacker.net/asm/ayuda_con_funcion_split-t386501.15.html

Un saludo.


Título: Re: [Ayuda-C] Bucle infinito
Publicado por: rir3760 en 11 Octubre 2013, 04:02 am
El error principal ya lo indico 0xDani:
Quizá sea porque siguiente_lexema() llama a buscar_caracter(), que a su vez llama a siguiente_lexema()...
Y como en ningún momento se modifica el valor de la variable "global" "pos" ello resulta en una recursion infinita.

----

Aparte de eso se puede mejorar el programa:

* Falta consistencia, por ejemplo la función "buscar_caracter" accede directamente a la variable "global" "codigo" mientras que la función "siguiente_lexema" accede a la misma variable en base a su único parámetro.

* Deberías sustituir tus funciones "saber_letra" y "saber_numero" por "isalpha" e "isdigit" (prototipos en <ctype.h>). Si por alguna razón debes utilizar funciones propias puedes eliminar todos los paréntesis en ellas ya que no son necesarios.

* No generes tus propios mensajes de error si una función de entrada/salida falla, en su lugar relega ese trabajo a la función perror.

* Si abres un archivo en modo texto calcular su numero de caracteres en base a fseek+ftell es problemático en el mejor de los casos ya que al hacerlo obtienes la posición en bytes y esto puede funcionar o no.

Un ejemplo, si tenemos en MS Windows el archivo de texto:
Código:
1234
ABCD
Su contenido son dos lineas de texto, los diez caracteres requieren doce bytes (aquí el avance de linea se representa por la secuencia '\r' + '\n').

Si en ese SO ejecutamos el programa:
Código
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3.  
  4. #define NOM_ENTRADA  "Entrada.txt"
  5.  
  6. int main(void)
  7. {
  8.   FILE *entrada;
  9.   long num_bytes;
  10.   int num_chars;
  11.  
  12.   if ((entrada = fopen(NOM_ENTRADA, "rt")) == NULL){
  13.      perror(NOM_ENTRADA);
  14.      return EXIT_FAILURE;
  15.   }
  16.  
  17.   fseek(entrada, 0, SEEK_END);
  18.   num_bytes = ftell(entrada);
  19.  
  20.   rewind(entrada);
  21.   for (num_chars = 0; fgetc(entrada) != EOF; num_chars++)
  22.      ;
  23.   fclose(entrada);
  24.  
  25.   printf("numero de caracteres: %d\n", num_chars);
  26.   printf("numero de bytes: %ld\n", num_bytes);
  27.  
  28.   return EXIT_SUCCESS;
  29. }

Su salida es:
Código:
numero de caracteres: 10
numero de bytes: 12

En Linux no hay problema.

Un saludo


Título: Re: [Ayuda-C] Bucle infinito
Publicado por: Miky Gonzalez en 11 Octubre 2013, 15:51 pm
Gracias por la ayuda, al final resultó ser un problema estúpido. No aumentaba la variable pos en un determinado caso: que los paréntesis sean escritos juntos.

Respecto al comentario de rir3760:

La falta de consistencia está solucionado. Si te preguntas el porqué paso código como parámetro se debe a que en un futuro implementaré hilos para hacer el proceso más rápido (analizar varios archivos al mismo tiempo). Utilizo funciones (podría hacerlo con un #define pero aumentaría tamaño del código) propias porque me han resultado ser más rápidas (linux).
Los mensajes de error prefiero especificarlos yo, para tener un control absoluto del sistema de errores que se produzcan en el programa.
La forma en la que tengo implementada el código no podría admitir el uso de fgetc() para leer el tamaño del archivo, a no ser que la lectura y el analizado se hagan de una forma atómica. No se producirá ningún error, porque carácteres como ' ', '\t', '\n', '\r'... serán descartados de análisis.

El código resultante:

Código
  1. /*! Ejemplo de lector de archivo */
  2.  
  3. #include <stdio.h>
  4. #include <string.h>
  5. #include <stdlib.h>
  6.  
  7. #define NUM_INSTR 2
  8.  
  9. /*! Declaración de variables locales */
  10. enum {lex_PARI = '(', lex_PARD = ')', lex_LLAI = '{', lex_LLAD = '}'};
  11.  
  12. unsigned short int pos = 0x00;
  13. unsigned int codigo_volumen;
  14. char lista_instrucciones[NUM_INSTR][10] = {
  15. {"PRINT"},
  16. {"EXIT"}
  17. };
  18.  
  19. void siguiente_lexema(unsigned char *codigo);
  20. int saber_letra(char caracter);
  21.  
  22. void buscar_caracter(char caracter, unsigned char *codigo) {
  23. pos++;
  24. while((codigo[pos] != caracter) && (pos < codigo_volumen))
  25. siguiente_lexema(codigo);
  26.  
  27. if(pos == codigo_volumen)
  28. printf("Se esperaba '%c'", caracter);
  29. }
  30.  
  31. /*
  32.  * TODO: Identificar palabra reservada ó símbolo
  33. */
  34. // modo == 0 (si cadena)
  35. // modo == 1 (si entero)
  36. void identificador_cadena(unsigned char *codigo) {
  37. unsigned short int pos_buffer = 0;
  38. char *buffer = (char *)malloc(1 * sizeof(char));
  39.  
  40. while(saber_letra(codigo[pos])) {
  41. buffer[pos_buffer] = codigo[pos];
  42. pos++;
  43. pos_buffer++;
  44. buffer = (char *)realloc(buffer, (pos_buffer + 1) * sizeof(char));
  45. }
  46.  
  47. // Reutilizar variable pos_buffer
  48. for(pos_buffer = 0; pos_buffer < NUM_INSTR; pos_buffer++)
  49. if(strcmp(buffer, lista_instrucciones[pos_buffer]))
  50. printf("Encontrado %s en %s", lista_instrucciones[pos_buffer], buffer);
  51.  
  52. free(buffer);
  53. return;
  54. }
  55.  
  56. int saber_letra(char caracter) {
  57. return ((caracter >= 'A') && (caracter <= 'Z')) || ((caracter >= 'a') && (caracter <= 'z'));
  58. }
  59.  
  60. int saber_numero(char caracter) {
  61. return (caracter >= '0') && (caracter <= '9');
  62. }
  63.  
  64. void siguiente_lexema(unsigned char *codigo) {
  65. switch(codigo[pos]) {
  66. case lex_PARI:
  67. printf("Encontrado: %c\n", lex_PARI);
  68. buscar_caracter(lex_PARD, codigo);
  69. break;
  70. case lex_LLAI:
  71. printf("Encontrado: %c\n", lex_LLAI);
  72. buscar_caracter(lex_LLAD, codigo);
  73. break;
  74. default:
  75. if(saber_letra(codigo[pos]))
  76. identificador_cadena(codigo);
  77. else if(saber_numero(codigo[pos]))
  78. //identificador(codigo, 1);
  79. ;
  80. /*
  81. * TODO: Mostrar error. Ignorar espacios, salto de línea y tabulador
  82. */
  83. else
  84. ;
  85. break;
  86. }
  87.  
  88. pos++;
  89. return;
  90. }
  91.  
  92. int main(int argc, char **argv) {
  93. if(argc < 2) {
  94. printf("Uso: %s <archivo>\n", argv[0]);
  95. return 0;
  96. }
  97. FILE *codigo_archivo = fopen(argv[1], "r");
  98. if(!codigo_archivo) {
  99. printf("[ASM] Error al leer el archivo %s\n", argv[1]);
  100. return 0;
  101. }
  102.  
  103. /* Calcular tamaño (volumen) del código */
  104. fseek(codigo_archivo, 0, SEEK_END);
  105. codigo_volumen = ftell(codigo_archivo);
  106. rewind(codigo_archivo);
  107. if(!codigo_volumen) {
  108. printf("[ASM] No hay instrucciones...\n");
  109. fclose(codigo_archivo);
  110. return 0;
  111. }
  112.  
  113. /* Reservar el espacio necesario para almacenar
  114. * el código en memoria. Si existe, copiarlo. */
  115. unsigned char *codigo = (unsigned char *)malloc(codigo_volumen);
  116. if(!fread(codigo, 1, codigo_volumen, codigo_archivo)) {
  117. printf("[ASM] Error en lectura de archivo...\n");
  118. fclose(codigo_archivo);
  119. return 0;
  120. } else fclose(codigo_archivo);
  121.  
  122. while(pos < codigo_volumen)
  123. siguiente_lexema(codigo);
  124.  
  125. return 0;
  126. }
  127.