[Ayuda-C] Bucle infinito

Iniciado por Miky Gonzalez, 10 Octubre 2013, 18:43 PM

0 Miembros y 3 Visitantes están viendo este tema.

Miky Gonzalez

Estaba programando en C un ensamblador para programas de mi máquina virtual (decidí continuar un proyecto). Tengo el siguiente código:

/*! Ejemplo de lector de archivo */

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

/*! Declaración de variables locales */
enum {lex_PARI = '(', lex_PARD = ')', lex_LLAI = '{', lex_LLAD = '}'};
unsigned short int pos = 0x00;
unsigned int codigo_volumen;
unsigned char *codigo;
FILE *codigo_archivo;

void siguiente_lexema(unsigned char *codigo);

void buscar_caracter(char caracter) {
while((caracter != codigo[pos]) && (pos < codigo_volumen)) {
siguiente_lexema(codigo);
pos++;
}
if(pos == codigo_volumen)
printf("Se esperaba '%c'", caracter);
}

// modo == 0 (si cadena)
// modo == 1 (si entero)
void identificador(unsigned char *codigo, char modo) {

return;
}

int saber_letra(char caracter) {
return (((caracter >= 'A') && (caracter <= 'Z')) || ((caracter >= 'a') && (caracter <= 'z')));
}

int saber_numero(char caracter) {
return ((caracter >= '0') && (caracter <= '9'));
}

void siguiente_lexema(unsigned char *codigo) {
switch(codigo[pos]) {
case lex_PARI:
printf("Encontrado: %c\n", lex_PARI);
buscar_caracter(lex_PARD);
break;
case lex_LLAI:
printf("Encontrado: %c\n", lex_LLAI);
buscar_caracter(lex_LLAD);
break;
default:
if(saber_letra(codigo[pos]))
identificador(codigo, 0);
else if(saber_numero(codigo[pos]))
identificador(codigo, 1);
/*
* TODO: Mostrar error. Ignorar espacios, salto de línea y tabulador
*/
else
;
break;
}

return;
}

int main(int argc, char **argv) {
if(argc < 2) {
printf("Uso: %s <archivo>\n", argv[0]);
return 0;
}
codigo_archivo = fopen(argv[1], "r");
if(!codigo_archivo) {
printf("[ASM] Error al leer el archivo %s\n", argv[1]);
return 0;
}

/* Calcular tamaño (volumen) del código */
fseek(codigo_archivo, 0, SEEK_END);
codigo_volumen = ftell(codigo_archivo);
rewind(codigo_archivo);
if(!codigo_volumen) {
printf("[ASM] No hay instrucciones...\n");
fclose(codigo_archivo);
return 0;
}

/* Reservar el espacio necesario para almacenar
* el código en memoria. Si existe, copiarlo. */
codigo = (unsigned char *)malloc(codigo_volumen);
if(!fread(codigo, 1, codigo_volumen, codigo_archivo)) {
printf("[ASM] Error en lectura de archivo...\n");
fclose(codigo_archivo);
return 0;
} else fclose(codigo_archivo);

while(pos < codigo_volumen) {
siguiente_lexema(codigo);
pos++;
}

return 0;
}


El problema está en que dado un archivo:

Código (ini) [Seleccionar]
{
{
{
( {} )( } } }


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?
Mi blog personal, con información acerca de programación, seguridad, desarrollo y electrónica:

EN CONSTRUCCIÓN

0xDani

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.
I keep searching for something that I never seem to find, but maybe I won't, because I left it all behind!

I code for $$$
Hago trabajos en C/C++
Contactar por PM

cpu2

#2
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.


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


Un saludo.

rir3760

El error principal ya lo indico 0xDani:
Cita de: 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()...
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:
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:
#include <stdio.h>
#include <stdlib.h>

#define NOM_ENTRADA  "Entrada.txt"

int main(void)
{
   FILE *entrada;
   long num_bytes;
   int num_chars;
   
   if ((entrada = fopen(NOM_ENTRADA, "rt")) == NULL){
      perror(NOM_ENTRADA);
      return EXIT_FAILURE;
   }
   
   fseek(entrada, 0, SEEK_END);
   num_bytes = ftell(entrada);
   
   rewind(entrada);
   for (num_chars = 0; fgetc(entrada) != EOF; num_chars++)
      ;
   fclose(entrada);
   
   printf("numero de caracteres: %d\n", num_chars);
   printf("numero de bytes: %ld\n", num_bytes);
   
   return EXIT_SUCCESS;
}


Su salida es:
numero de caracteres: 10
numero de bytes: 12


En Linux no hay problema.

Un saludo
C retains the basic philosophy that programmers know what they are doing; it only requires that they state their intentions explicitly.
--
Kernighan & Ritchie, The C programming language

Miky Gonzalez

#4
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:

/*! Ejemplo de lector de archivo */

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

#define NUM_INSTR 2

/*! Declaración de variables locales */
enum {lex_PARI = '(', lex_PARD = ')', lex_LLAI = '{', lex_LLAD = '}'};

unsigned short int pos = 0x00;
unsigned int codigo_volumen;
char lista_instrucciones[NUM_INSTR][10] = {
{"PRINT"},
{"EXIT"}
};

void siguiente_lexema(unsigned char *codigo);
int saber_letra(char caracter);

void buscar_caracter(char caracter, unsigned char *codigo) {
pos++;
while((codigo[pos] != caracter) && (pos < codigo_volumen))
siguiente_lexema(codigo);

if(pos == codigo_volumen)
printf("Se esperaba '%c'", caracter);
}

/*
* TODO: Identificar palabra reservada ó símbolo
*/
// modo == 0 (si cadena)
// modo == 1 (si entero)
void identificador_cadena(unsigned char *codigo) {
unsigned short int pos_buffer = 0;
char *buffer = (char *)malloc(1 * sizeof(char));

while(saber_letra(codigo[pos])) {
buffer[pos_buffer] = codigo[pos];
pos++;
pos_buffer++;
buffer = (char *)realloc(buffer, (pos_buffer + 1) * sizeof(char));
}

// Reutilizar variable pos_buffer
for(pos_buffer = 0; pos_buffer < NUM_INSTR; pos_buffer++)
if(strcmp(buffer, lista_instrucciones[pos_buffer]))
printf("Encontrado %s en %s", lista_instrucciones[pos_buffer], buffer);

free(buffer);
return;
}

int saber_letra(char caracter) {
return ((caracter >= 'A') && (caracter <= 'Z')) || ((caracter >= 'a') && (caracter <= 'z'));
}

int saber_numero(char caracter) {
return (caracter >= '0') && (caracter <= '9');
}

void siguiente_lexema(unsigned char *codigo) {
switch(codigo[pos]) {
case lex_PARI:
printf("Encontrado: %c\n", lex_PARI);
buscar_caracter(lex_PARD, codigo);
break;
case lex_LLAI:
printf("Encontrado: %c\n", lex_LLAI);
buscar_caracter(lex_LLAD, codigo);
break;
default:
if(saber_letra(codigo[pos]))
identificador_cadena(codigo);
else if(saber_numero(codigo[pos]))
//identificador(codigo, 1);
;
/*
* TODO: Mostrar error. Ignorar espacios, salto de línea y tabulador
*/
else
;
break;
}

pos++;
return;
}

int main(int argc, char **argv) {
if(argc < 2) {
printf("Uso: %s <archivo>\n", argv[0]);
return 0;
}
FILE *codigo_archivo = fopen(argv[1], "r");
if(!codigo_archivo) {
printf("[ASM] Error al leer el archivo %s\n", argv[1]);
return 0;
}

/* Calcular tamaño (volumen) del código */
fseek(codigo_archivo, 0, SEEK_END);
codigo_volumen = ftell(codigo_archivo);
rewind(codigo_archivo);
if(!codigo_volumen) {
printf("[ASM] No hay instrucciones...\n");
fclose(codigo_archivo);
return 0;
}

/* Reservar el espacio necesario para almacenar
* el código en memoria. Si existe, copiarlo. */
unsigned char *codigo = (unsigned char *)malloc(codigo_volumen);
if(!fread(codigo, 1, codigo_volumen, codigo_archivo)) {
printf("[ASM] Error en lectura de archivo...\n");
fclose(codigo_archivo);
return 0;
} else fclose(codigo_archivo);

while(pos < codigo_volumen)
siguiente_lexema(codigo);

return 0;
}
Mi blog personal, con información acerca de programación, seguridad, desarrollo y electrónica:

EN CONSTRUCCIÓN