Pointer from struct to struct

Asked

Viewed 793 times

3

Hello, I had asked a previous question about this problem, however I was able to solve but 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 why it is wrong.

Follows 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;
}

Then after testing several ways to enter the data I realized that in the method "inse_test()" it error in the line:

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

I would like someone to explain me this mistake, because how to solve 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 why 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 have already learned.

From now on, thank you very much.

1 answer

2


Your code error does not happen on the line

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

The problem is further forward in:

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

The content is that tmp->next has garbage, that is, has a different value than zero, and when tmp becomes tmp->next, there is no next to check on while. This generates memory error.

When working with stacks, lists and pointers, you usually start them with 0 so that the structures do not return true so avoid mistakes of this kind.

Since you are not using pointers to instantiate your list, you should assign NULL or 0 at the 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 you get to the loop, you will identify the zero and you will close.

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

Another observation.

inserts:

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

That condition should return 1 case ps is empty, but ps is never empty as it is possible to access the fields p, num e next, the right of 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 the methods, would become people **ps (double pointer).

void insere_people(people **ps){

And to access your item to pick up the desired value you have to collect the address within the pointer

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

What is a double pointer?

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

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

As we already know, the variable stores a value and the simple pointer stores the memory address of the variable. Then the double pointer is responsible for saving the memory addresses of the variables. 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 that is, the memory address that is saved from the memory address of var is saved in ptr_d

To better understand with examples, visit:

Main File - On github

Stack Struct - On github

  • Could you explain to me what a double pointer would be and what would be the difference between it and a normal pointer? Because this is a code isolated only from a problem I had and managed to solve, however solve without understanding it is no use, if you want I can post the original code here (I left it on github, then it has the previous versions).

  • 1

    @Bruno, I added an explanation about the double pointer, I hope I didn’t get too abstract.

  • Okay, correct me if I’m wrong, but from what I understand in case I do an assignment **ptr_d = 7 it will change all the other values, right? So a double pointer is nothing more than a simple pointer extension, but when and why is it used?

  • 1

    exactly the value is changed like this. The double pointer is usually using when one wants to change a pointer, as lists are usually done with a yes pointer (in most cases), it is using the double pointer to change them, as in the example on the link, where the pointer is started with NULL to set an end.

  • Right, but now I got a little confused, I understood the HOW to use, but I didn’t understand the WHY to use, because I couldn’t use a simple pointer for this same example? What makes it more accurate, or necessary to create a pointer to this pointer?

  • 1

    @Bruno, The first reason is the semantics of the code, the second is the pointer be responsible for storing addresses, it is very likely that of memory error when trying to put an address in a variable that does not support it, because each type of variable needs to reserve an amount x memory, and pointers already have enough space for this, so we use dual pointers to store pointers.

  • I think I got it, but let’s see, the double pointer actually will allocate a new memory space to avoid conflicts or buffer bursts, that’s it?

  • 1

    Not conflicts, but buffers yes.

  • So I think now yes I understood, I will try to use in future projects, because then yes I can take the proof of this kkkk, thank you very much.

Show 4 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.