[C] Apuntadores a estructuras

Iniciado por edr89, 4 Octubre 2013, 18:25 PM

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

edr89

Hola, estoy construyendo una lista de numeros pero al compilar me da error por el operador ->, alguien nota el error?
#include <stdio.h>
#include <stdlib.h>
typedef struct st_num //Plantilla para numero aleatorio.
{
    int rand_num;
    struct st_num *ptr_next;
}st_num;

typedef struct
{
    st_num *head;
    st_num *last;

}fila;

int main()
{
    int i;
    int respuesta_num; //Dato tecleado por usuario
    char key_buff[40];
    st_num *nodo;
    menu_principal(); //Borrar pantalla y desplegar texto
    scanf("%d",&respuesta_num);
    gets(key_buff); //limpiar buffer
    fila->head = NULL;
    fila->last = NULL;

    for(i=0;i<respuesta_num;i++)
    {
        nodo = (st_num *) malloc(sizeof(st_num)); //Direccion de bloque creado
        nodo->rand_num = rand()%100+1;
            if(i=0) //Edicion de primer elemento
            {
                fila->head = nodo;
                fila->last = nodo;
                nodo->ptr_next = NULL;
            }
            if(nodo->ptr_next != NULL)
            {
                nodo ->ptr_next = fila->head;
            }
        fila->head = nodo;
        printf("%d\n",nodo->ptr_next);
    }
}

rir3760

El error se debe en parte a la declaración:
typedef struct
{
   st_num *head;
   st_num *last;
}fila;

Con ella no estas declarando una variable sino un alias o nombre alternativo "fila". Para declarar una variable con ese nombre debes cambiar la linea eliminando la palabra reservada "typedef":
struct
{
   st_num *head;
   st_num *last;
}fila;

Y como es una variable y no un puntero debes cambiar en tu programa todas las instancias de "fila->" por "fila.".

También hay otros errores como:
if(i=0) //Edicion de primer elemento
Ahí estas utilizando el operador de asignación "=" cuando debería ser el de comparación "==".

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

edr89

Como debo hacer para poder usar el operador flecha? no sirve con estructuras?

tendria que declarar:
typedef fila *nodo NODO
y entonces puedo usar
NODO->head; :huh:

he cambiado los operadores por puntos y compila aunque al terminar el programa regresa el numero que ha tecleado el usuario.

eferion

Cita de: edr89 en  4 Octubre 2013, 19:04 PM
Como debo hacer para poder usar el operador flecha? no sirve con estructuras?

tendria que declarar:
typedef fila *nodo NODO

Esa línea no tiene ningún sentido.

Si tu tienes:


struct
{
   st_num *head;
   st_num *last;
}fila;


No puedes crear nuevas instancias de la estructura, ya que la estructura no tiene nombre. Para poder crear instancias nuevas tienes que darle un nombre a la estructura:


struct fila
{
   st_num *head;
   st_num *last;
};


Entonces ahora ya si puedes hacer:


typedef struct fila NODO;


Aunque si lo que quieres es definir un tipo para el puntero tendría que ser:

typedef struct fila* NODO;

Con esto usar los nodos es tan sencillo como:



NODO nodo; // has declarado una variable, llamada nodo, que es un puntero a una estructura de tipo fila.

nodo->head = NULL;
nodo->last = NULL;


Tu piensa que el operador flecha sirve para acceder al contenido de los punteros... si no tienes punteros no tiene sentido el operador flecha. Con esto mismo también te digo que a veces es mejor evitar el uso de memoria dinámica...

edr89

Cita de: eferion en  4 Octubre 2013, 19:18 PM
Tu piensa que el operador flecha sirve para acceder al contenido de los punteros... si no tienes punteros no tiene sentido el operador flecha. Con esto mismo también te digo que a veces es mejor evitar el uso de memoria dinámica...

Entiendo, la cuestion es que debo trabajar con memoria dinamica porque es el tema que estamos viendo, el programa corre bien al generar e imprimir los numeros pero despues de preguntar por el numero natural (linea 53) truena. Lo que quiero hacer es eliminar datos mayores .

typedef struct st_num //Plantilla para numero aleatorio.
{
   int rand_num;
   struct st_num *ptr_next;
}st_num;

struct
{
   st_num *head;
   st_num *last;

}fila;

void menu_principal();
void menu_principal()
{
    system("CLS");
    puts("Generador de numeros aleatorios\n\n");
    printf("Escribe el numero de datos a generar: ");
}

int main()
{
   int i;
   int respuesta_num; //Dato tecleado por usuario
   char key_buff[40];
   st_num *nodo;
   st_num *nodo_aux;
   menu_principal(); //Borrar pantalla y desplegar texto
   scanf("%d",&respuesta_num);
   gets(key_buff); //limpiar buffer
   fila.head = NULL;
   fila.last = NULL;

   for(i=0;i<respuesta_num;i++)
   {
       nodo = (st_num *) malloc(sizeof(st_num)); //Se crea bloque
       nodo->rand_num = rand()%100+1;           //Se asigna numero
           if(i==0)                             //Condicion para primer elemento
           {
               fila.head = nodo;
               fila.last = nodo;
               nodo->ptr_next = NULL;
           }
           if(nodo->ptr_next != NULL)
           {
               nodo ->ptr_next = fila.head;
           }
       fila.head = nodo;
       printf("%d\n",nodo->rand_num);
   }
   printf("Ahora escribe un numero natural menor a 100: ");
   scanf("%d",&respuesta_num);
   gets(key_buff);
   while(nodo->ptr_next!=NULL)
   {
       if((nodo->rand_num>respuesta_num)&&nodo==fila.head)
       {
           fila.head = (fila.head)->ptr_next;
           free(nodo);
           nodo = fila.head;
       }
       else if(nodo->rand_num>respuesta_num)
       {
           nodo_aux = nodo->ptr_next;
           free(nodo);
           nodo = nodo_aux;
       }
       nodo = nodo->ptr_next;
   }
    //Imprimir nueva lista...
}

rir3760

Algo que falta en tu programa es la inclusión de los encabezados <stdio.h> y <stdlib.h>.

El primer error importante se encuentra en el bucle donde creas la lista con números aleatorios:
for (i = 0; i < respuesta_num; i++){
   nodo = (st_num *) malloc(sizeof(st_num));
   
   nodo->rand_num = rand() % 100 + 1;
   if (i == 0){
      fila.head = nodo;
      fila.last = nodo;
      nodo->ptr_next = NULL;
   }
   if (nodo->ptr_next != NULL){ /* <== */
      nodo ->ptr_next = fila.head;
   }
   fila.head = nodo;
   printf ("%d\n", nodo->rand_num);
}

Al reservar memoria con malloc el contenido del bloque es "no definido", puede ser cualquier valor incluyendo NULL (0 en contexto de punteros). En otras palabras no hay garantías de que el campo "ptr_next" en los nodos después del primero se ajuste correctamente.

Ese bucle se debe cambiar a:
   for (i = 0; i < respuesta_num; i++){
      nodo = malloc(sizeof *nodo);
      nodo->rand_num = rand() % 100 + 1;
     
      if (i == 0){
         fila.last = nodo;
         nodo->ptr_next = NULL;
      }else
         nodo ->ptr_next = fila.head;
      fila.head = nodo;
      printf ("%d\n", nodo->rand_num);
   }


En cuanto al bloque donde se eliminan los elementos mayores al indicado este tiene varios errores graves ya que el programa va a reventar con una lista vacía y también si la lista contiene un solo nodo y este debe eliminarse. Otro detalle ahí es que no verificas si se debe eliminar el primer nodo.

Una forma de eliminar todos los nodos cuyo valor sea mayor que el indicado es utilizando un puntero de tipo "st_num **p" con la ventaja de que este permite evitar el caso especial del primer nodo. Tu programa con los cambios:
#include <stdio.h>
#include <stdlib.h>

typedef struct st_num {
   int rand_num;
   struct st_num *ptr_next;
} st_num;

struct {
   st_num *head;
   st_num *last;
   
} fila;

void menu_principal(void);

int main(void)
{
   int i;
   int respuesta_num;
   char key_buff[40];
   st_num *nodo;
   st_num **p;
   
   menu_principal();
   scanf("%d", &respuesta_num);
   gets(key_buff);
   
   for (i = 0; i < respuesta_num; i++){
      nodo = malloc(sizeof *nodo);
      nodo->rand_num = 1 + rand() % 2;
     
      if (i == 0){
         fila.last = nodo;
         nodo->ptr_next = NULL;
      }else
         nodo ->ptr_next = fila.head;
      fila.head = nodo;
   }
   
   i = 0;
   for (nodo = fila.head; nodo != NULL; nodo = nodo->ptr_next, i++)
      printf(" %d", nodo->rand_num);
   printf(" (%d)\n", i);
   
   printf("Ahora escribe un numero natural menor a 100: ");
   scanf("%d", &respuesta_num);
   gets(key_buff);
   
   /* Eliminamos todos los nodos mayores que "respuesta_num" */
   p = &fila.head;
   while (*p != NULL)
      if ((*p)->rand_num > respuesta_num){ /* Eliminar actual */
         nodo = *p;
         *p = (*p)->ptr_next;
         free(nodo);
      }else /* Siguiente nodo */
         p = &(*p)->ptr_next;
   /* Ajustamos el valor del campo fila.last */
   if (fila.head == NULL)
      fila.last = NULL;
   else {
      fila.last = fila.head;
      while (fila.last->ptr_next != NULL)
         fila.last = fila.last->ptr_next;
   }
   
   i = 0;
   for (nodo = fila.head; nodo != NULL; nodo = nodo->ptr_next, i++)
      printf(" %d", nodo->rand_num);
   printf(" (%d)\n", i);
   
   return 0;
}

void menu_principal(void)
{
   system ("CLS");
   puts ("Generador de numeros aleatorios\n\n");
   printf ("Escribe el numero de datos a generar: ");
}


Por ultimo deberías evitar el uso de la función gets, mas información en la pagina |Lo que no hay que hacer en C/C++. Nivel basico|.

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

edr89

#6
edito:
he reescrito a una nueva version, la finalidad es la misma
el primer código que hice me termino disgustando.

Lo compilé y aparentemente no truena todo corre como debe, me ayudan a detectar posibles fallas?


#include <stdio.h>
#include <stdlib.h>
struct lista
{
    char ch;
    struct lista *next_rec;
};
typedef struct lista LIST;
typedef LIST *LISTPTR;

LISTPTR add_to_list( char, LISTPTR );
void show_list(void);
void free_memory_list(void);
void borrar_mayores(int respuesta,int natural);

LISTPTR first = NULL;

int main(void)
{
    LISTPTR rec_addr;
    int i=0;
    int respuesta, natural;
    rec_addr = add_to_list(rand()%100+1, (LISTPTR)NULL);
    first = rec_addr;
    puts("Numeros aleatorios\n");
    //Imprimir menu principal
    //Input total de datos
    respuesta = input_error();
    while(i++<(respuesta-1))
    {
        rec_addr = add_to_list(rand()%100+1,rec_addr);
    }
    show_list();

    //Numero natural
    printf("Escribe un natural menor a 100: ");
    natural = input_error();
    rec_addr = first;
    borrar_mayores(respuesta,natural);
    show_list();
    getch();
    free_memory_list();
    return(0);
    //completar funcion main
}

LISTPTR add_to_list(char ch, LISTPTR prev_rec)
{
    LISTPTR new_rec = NULL;
    new_rec = (LISTPTR) malloc(sizeof(LIST));
    if(!new_rec)
    {
        printf("Error al reservar memoria");
        exit(1);
    }
    new_rec->ch = ch;
    new_rec->next_rec = NULL;
    if(prev_rec)
    {
        prev_rec->next_rec = new_rec;
    }
    return(new_rec);
}

void borrar_mayores(int respuesta,int natural)
{
    LISTPTR new_rec = first;
    LISTPTR aux = new_rec;
    if(new_rec->next_rec==NULL)
    {
        if(new_rec->ch>natural)
        {
            free(new_rec);
            first=NULL;
            printf("\n\nLa nueva lista no tiene datos. ");
            getch();
            exit(0);
        }
    }
    while(new_rec==first)
    {
        if(new_rec->ch>natural)
        {
            first = new_rec->next_rec;
            free(new_rec);
            new_rec = first;
            aux = first;
        }
        else
        {
            aux = new_rec;
            new_rec = new_rec->next_rec;
        }
    }
    while(new_rec->next_rec!=NULL)
    {

        if(new_rec->ch>natural)
        {
            aux->next_rec=new_rec->next_rec;
            free(new_rec);
            new_rec = aux->next_rec;

        }
        else
        {
            aux = new_rec;
            new_rec = new_rec->next_rec;
        }
    }
}

void show_list()
{
    LISTPTR cur_ptr;
    int counter = 1;
    printf("Rec addr  Position  Data  Next rec addr\n\n");
    cur_ptr = first;
    while(cur_ptr)
    {
        printf("  %x   ",cur_ptr);
        printf("     %2i       %d",counter++,cur_ptr->ch);
        printf("      %x   \n",cur_ptr->next_rec);
        cur_ptr = cur_ptr->next_rec;
    }
}

void free_memory_list()
{
    LISTPTR cur_ptr,next_rec;
    cur_ptr = first;
    while (cur_ptr)
    {
        next_rec = cur_ptr->next_rec;
        free(cur_ptr);
        cur_ptr = next_rec;
    }
}

int input_error(void)
{
    int respuesta;
    char key_buff[50];
    printf("Escribe el numero de datos a generar: ");
    while((scanf("%d",&respuesta) != 1)||respuesta==0) //en caso de ñ
        {
            while (getchar() != '\n');
            printf ("\nDato no valido intenta de nuevo: ");
        }
    gets(key_buff);
    return respuesta;
}


Saludos!

rir3760

* Faltan los prototipos de la funciones "input_error" y "getch", si bien técnicamente no son errores se recomienda declarar toda función antes de su uso. En tu caso la solución es colocar el prototipo de "input_error" y la inclusión del encabezado conio al principio del programa.

* Cuando se desea imprimir un puntero se debe convertir este explícitamente al tipo "void *" y utilizar el especificador de formato "%p" con printf, estas dos sentencias en la función show_list:
printf("  %x   ", cur_ptr);
/* ... */
printf("      %x   \n", cur_ptr->next_rec);

Hay que sustituirlas por:
printf("  %p   ", (void *) cur_ptr);
/* ... */
printf("      %p   \n", (void *) cur_ptr->next_rec);


* En la función main debes utilizar nombres mas claros, en lugar de "rec_addr" y "add_to_list" los nombres "last" y "add_after" indican mejor el uso de la variable y función. Ahí la sentencia:
rec_addr = first;
Hay que eliminarla ya que el valor de "rec_addr" no se utiliza en el resto de la función.

* La función "input_error" siempre imprime el texto "Escribe el numero de datos a generar" y seria mejor reescribirla desde cero.

* Si ejecutas el programa varias veces te darás cuenta de que la eliminación de los nodos con un valor mayor al indicado no funciona correctamente.

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

edr89

#8
edito:
ya tengo la version final, la funcion que borra los elementos es la adaptación del primero código al segundo  ;D

void borrar_mayores(int natural)
{
    LISTPTR *p;
    LISTPTR aux;
    p=&first;
    if(first->next_rec==NULL)
    {
        if ((*p)->ch> natural)
        { /* Eliminar elemento unico de lista */
            aux = first;
            first = NULL;
            *p = NULL;
            free(aux);
        }
    }
    while (*p != NULL)
    {
        if ((*p)->ch> natural)
        { /* Eliminar actual */
            aux = *p;
            *p = (*p)->next_rec;
            free(aux);
        }
        else /* Siguiente nodo */
        {
            p = &(*p)->next_rec;
        }

    }
}


rir3760

Cita de: edr89 en  6 Octubre 2013, 21:24 PMya tengo la version final, la funcion que borra los elementos es la adaptación del primero código al segundo
En esa función no necesitas de dos bloques ya que el segundo puede (y va) a procesar todos los nodos, a eso me refería con el comentario sobre evitar casos especiales.

Con solo el bloque necesario (el segundo) la función se reduce a:
void borrar_mayores(int natural)
{
   LISTPTR *p;
   LISTPTR aux;
   
   p = &first;
   while (*p != NULL)
      if ((*p)->ch > natural){
         aux = *p;
         *p = (*p)->next_rec;
         free(aux);
      }else
         p = &(*p)->next_rec;
}


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