Pointer to struct for struct

3
Hello, I had asked a previous question about this problem, but I managed to solve it but did not learn about the problem itself so I tried to isolate it by creating a specific code for this problem and I would like someone to teach me what is happening and because it is wrong.

Follow the code:

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

typedef struct Person
{
    char name[256];
    //char *name;
} person;

typedef struct Population
{
    person p;
    int num;
    struct Population *next;
}people;

void insere_person(person *p){
   printf("insira um nome: ");
   scanf(" %s", p->name);
}

void insere_people(people *ps){
    printf("insira um nome: ");
    scanf(" %s", ps->p.name);
    printf("insira um numero: ");
    scanf(" %d", &ps->num);
}

void insere_person_to_people(person *p, people *ps){
    insere_person(p);
    printf("insira um numero: ");
    scanf(" %d", &ps->num);
    ps->p = *p;
}

short vazia(people *ps){
    if(ps->next == NULL){ return 1; }
    else{ return 0; }
}

void insere_teste(people *ps){
    //char nome[256];
    people *new_ps = (people *)malloc(sizeof(people));
    printf("digite um nome:");
    scanf(" %s", ps->p.name);
    //scanf(" %s", nome);

    new_ps->p = ps->p;
    new_ps->next = NULL;

    if(vazia(ps)){
        ps->next = new_ps;
    }
    else{
        people *tmp = ps->next;
        while(tmp->next != NULL){
            tmp = tmp->next;
        }
        tmp->next = new_ps;
    }
}

void person_to_string(person *p){
    printf("person{nome:%s}\n", p->name);
}

void people_to_string(people *ps){
    printf("people{nome:%s, num:%d}\n", ps->p.name, ps->num);
}

void all_to_string(people *ps){
    if(vazia(ps)){
        printf("ninguem!\n");
        return;
    }
    else{
        people *tmp = ps->next;
        while(tmp != NULL){
            people_to_string(tmp);
            tmp = tmp->next;
        }
    }
}

void menu(person *p, people *ps){
    int op = -1;

    while(op != 0){
        printf("0 - sair\
        \n1 - insere pessoa\
        \n2 - insere populacao\
        \n3 - mostra pessoa\
        \n4 - mostra populacao\
        \nopcao: ");
        scanf(" %d", &op);

        switch(op){
        case 0:
            break;
        case 1:
            insere_person(p);
            break;
        case 2:
            //insere_people(ps); // funciona
            //insere_person_to_people(p, ps); // funciona
            insere_teste(ps); // não funciona
            break;
        case 3:
            person_to_string(p);
            break;
        case 4:
            //people_to_string(ps);
            all_to_string(ps);
            break;
        default:
            printf("Opcao invalida.\n");
            break;
        }
    }
}

int main()
{
    person p;
    people ps;

    menu(&p, &ps);

    return 0;
}

So after testing several ways to insert the data I noticed that in the method "insert_test ()" it of the error in the line:

scanf(" %s", ps->p.name);

I would like someone to explain this error to me, because how to solve it I already know, but it does not help me to know how to solve without understanding what happens.

Another point I would like to learn is because they are all being modified together, I know it is a pointer that is pointing to the same address, but I would like a more didactic explanation for this, because in practice I already learned.

Thank you in advance.

    
asked by anonymous 27.05.2016 / 06:39

1 answer

2

Your code error does not happen on the line

scanf(" %s", ps->p.name);

The problem is more ahead in:

while(tmp->next != NULL){
    tmp = tmp->next;
}

The content is that tmp->next has garbage, that is, it has a value other than zero , and when tmp becomes tmp->next there is no next to check in while . This causes the memory error.

When working with stacks, lists, and pointers, you often start them with 0 so that the structures do not return true , thus avoiding errors of that type.

As you are not using pointers to instantiate your list, you should assign NULL 0 ps.next , so it will indicate that there is no next item in your list.

main:

people ps;
// ps.next possui lixo (valor != 0)
ps.next = 0; // ao atribuir 0 para ps.next, é definido o final da lista

So when it hits the loop, it will identify zero and it will wax.

while(tmp->next != NULL){ // NULL == 0
    tmp = tmp->next;
}

Another note.

insere_teste:

if(vazia(ps)){
    ps->next = new_ps;
}

This condition should return 1 if ps is empty, but ps is never empty, since it is possible to access the p, num e next fields, the right one in an empty list would be a pointer with the value 0

int main(){
    person p;
    //people ps; // Lista com 1 registro
    people *ps = 0; // lista vazia

    menu(&p, &ps);
    return 0;
}
  

What would change in my code?

Instead of using people *ps in methods, it would be people **ps (double pointer).

void insere_people(people **ps){

And to access your item to get the desired value you have to collect the address inside the pointer

(*ps)->next; // pega o valor dentro do ponteiro duplo
  

What is a double pointer?

The double pointer is a pointer that holds other pointer (Save memory address). Your statement is made by ** .

int var;
int *ptr; // ponteiro normal
int **ptr_d; // ponteiro duplo

As you know, the variable holds a value and the simple pointer stores the memory address of the variable. Then the double pointer is responsible for storing the variable memory addresses. Ex:

int var = 6;
int *ptr = &var;
int **ptr_d = &ptr;
printf("%i - %i - %i", var, *ptr, **ptr_d); // %i equivale a %d

ptr_d - > ptr - > var ptr_d points to ptr that points to var ie the memory address that is saved the memory address of var is saved in ptr_d

To better understand examples, go to:

Main File - On github

Stack Struct - On github

    
27.05.2016 / 07:27