Doubt about Struct and Pointer for Struct

Asked

Viewed 7,927 times

4

I’m having doubts in my code about structs and pointers for structs, the doubts are as follows:

1) Because I don’t need to put (->) before speed, for example ( p->attributes->speed) in printf(" tVelocity: %d n n", p->attributes.) ?

2) If 'p' is a dynamically allocated vector, why do I need to put "&" in imprime_pokemon_name(&p[indice_pokemon]) and imprime_pokemon_atak(&p[indice_pokemon].attacks[indice_attack]) ? Normally I define my functions with vectors, in the function int main, without the '&', as, func(vector).

3) The void function imprime_pokemon_atak(Pokemon *p) has struct problems, but I have no idea what it is.

Excuse me for the size of the question, but I believe that these pieces are missing to fill my gaps in struct. Thank you!!

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

typedef struct{

    int HP;
    int ataque;
    int defesa;
    int ataque_especial;
    int defesa_especial;
    int velocidade;

}Atributos;

typedef struct{

    char nome_ataque[20];
    int poder_base;
    float acuracia;
    char classe;

}Ataques;

typedef struct{

    char nome[50];
    char tipo_primario[20];
    char tipo_secundario[20];
    Atributos atributos;
    Ataques ataques[4];

}Pokemon;

void imprime_pokemon_nome(Pokemon *p);
void imprime_pokemon_atak(Pokemon *p);


int main(){

    int option, i = 0, indice_pokemon, indice_ataque;
    Pokemon *p = NULL;
    scanf("%d", &option);

    while(option != 0){

        switch(option){

            case 1:
                p = (Pokemon*)realloc(p, (i+1)*sizeof(Pokemon));
                scanf(" %s %s %s %d %d %d %d %d %d", p[i].nome, p[i].tipo_primario, p[i].tipo_secundario, &p[i].atributos.HP, &p[i].atributos.ataque, &p[i].atributos.defesa,&p[i].atributos.ataque_especial, &p[i].atributos.defesa_especial, &p[i].atributos.velocidade);
                i++;
                break;

            case 2:
                scanf("%d %d", &indice_pokemon, &indice_ataque);
                scanf(" %s %d %f %c", p[indice_pokemon].ataques[indice_ataque].nome_ataque, &p[indice_pokemon].ataques[indice_ataque].poder_base, &p[indice_pokemon].ataques[indice_ataque].acuracia, &p[indice_pokemon].ataques[indice_ataque].classe);
                break;

            case 3:
                scanf("%d", &indice_pokemon);
                imprime_pokemon_nome(&p[indice_pokemon]);
                break;

            case 4:
                scanf("%d %d", &indice_pokemon, &indice_ataque);
                imprime_pokemon_atak(&p[indice_pokemon].ataques[indice_ataque]);
                break;
            }
            scanf("%d", &option);
    }

    return 0;
}

void imprime_pokemon_nome(Pokemon *p) {

    printf("Nome do Pokemon: %s\n", p->nome);
    printf("Tipo primario: %s\n", p->tipo_primario);
    printf("Tipo secundario: %s\n", p->tipo_secundario);
    printf("Status:\n");
    printf("\tHP: %d\n", p->atributos.HP);
    printf("\tAtaque: %d\n", p->atributos.ataque);
    printf("\tDefesa: %d\n", p->atributos.defesa);
    printf("\tAtaque Especial: %d\n", p->atributos.ataque_especial);
    printf("\tDefesa Especial: %d\n", p->atributos.defesa_especial);
    printf("\tVelocidade: %d\n\n", p->atributos.velocidade);

}

void imprime_pokemon_atak(Pokemon *p){

    printf("Nome do Ataque: %s\n", p->ataques->nome_ataque);
    printf("Poder base: %d\n", p->ataques->poder_base);
    printf("Acuracia: %f\n", p->ataques->acuracia);
    printf("Classe: %c\n\n", p->ataques->classe);
}
  • 1

    When calling any of the stack allocation functions (malloc, realloc, calloc) 1) always check (! = NULL) the value returned to ensure the operation has been successful.

  • 1

    By calling any of the function families scanf (), always check the returned value (not parameter values) to ensure the operation has been successful

  • By calling scanf () with a "%s" specifier, always include a modifier that is 1 less than the input buffer length to avoid buffer overflow.

3 answers

4

Victor My answer will be a little long, but I like to explain the details, because without this conceptual basis you can face new problems. Believe take a little more time reading this long answer and save more time in the future.

YOUR DOUBT: 1) Because I don’t need to put (->) before speed, for example ( p->attributes->speed) in: printf(" tVelocity: %d n n", p->attributes.) ?

To answer this question we need to recall some concepts

POINTERS

The C language has many different ways of doing the same operation. Much of this came out of the need to optimize source code and also speed up programming.

When working with pointer variables that store a single memory address within it ,some special symbols are required. Namely:

& - Meaning: memory address of

*(asterisk) - Means pointer dereferencing (I explain this further below)

Note the following code snippet:

int x; declares and requests memory space to allocate a variable of type int

int *p; declares a pointer variable capable of pointing to a structured memory region as integer(int)

p = &x; the pointer variable receives the memory address of the variable x

*p = 10;

In this code snippet *p = 10 the pointer is de-referenced: that is, the content of the memory region pointed by the pointer is set to 10.

The * asterisk is like saying: "pointer go there at the memory address you are pointing to and put the value 10.

VECTORS AND POINTERS

When working with vectors(arrays) in the C language, the process is the same. The difference is that vectors have memory compartments separated by an index and accessed via brackets[ ] Let’s go to one more code:

#include <stdio.h>
#include <locale.h> /*para acentuação*/
#include <stdlib.h>

int main(int argc, char** argv)
{
    system("CLS"); //limpa tela no windows
    setlocale(LC_ALL, "");//acenturar a saída
    int vetorA[10];
    int *p;
    p = &vetorA[0]; /* coloca o endereço do primeiro elemento do vetor em p*/
    printf("\nEndereço de memória do vetor %p usando p", p);
    printf("\nEndereço de memória do vetor %p usando vetorA", vetorA);
    printf("\nEndereço de memória do vetor %p usando &vetorA[0]", &vetorA[0]);

    *(p + 0) = 10;
    /*Desrefenciamento: ou seja, aloque o valor 10 na posição de  memória contida
    em p + deslocamento 0(zero) */
    *(p + 1) = 11;
    /*Desrefenciamento: ou seja, aloque o valor 11 na posição de  memória contida
    em p + deslocamento 1(um) */
    printf("\nVetor no indice 0 vetorA[0] = %d e vetor índice 1 vetorA[1] = %d", *(p + 0), *(p + 1));
    printf("\nEndereço de memória do vetorA[7] %p \no mesmo que (p+7): %p)", &vetorA[7], (p + 7));
    /* *(p+0) pode ser simplificado para p[0] também são sinônimos e o uso dos colchetes
    foi criado para facilitar a escrita e otimizar código
    logo *(p+i) é o memso que p[i]. Assim *(p+1) = p[1]*/
    printf("\nVetor no indice 0 vetorA[0] = %d e vetor índice 1 vetorA[1] = %d \n \
        usando notação simplificada p[i]", p[0], p[1]);
    return 0;
}

when executing code appears: tela da execução do código acima

AN IMPORTANT OBSERVATION:

The memory address of a variable no matter its size in bytes will always be the address of its first byte. Remember that a pointer holds only one and only one memory address.

Another important observation is that in the C language the name of a vector is the same as the memory address of its first element. That is, vector A is the same as vector A[0];

And when you send a vector to a function it is always sent by reference because what is sent is its memory address at position [0].

Ex: funcao_qualquer(vector A) this call is the same as:

funcao_qualquer(&vetorA[0])

That answers your second question: 2) If 'p' is a dynamically allocated vector, why do I need to put "&" in imprime_pokemon_name(&p[indice_pokemon]) and imprime_pokemon_atak(&p[indice_pokemon].attacks[indice_attack]) ? Normally I define my functions with vectors, in the int main function, without the '&', as, func(vector).

This also explains why when using scanf with a string, which is a character array, we don’t need to use the symbol &

char frase[80] scanf("%[ n]s", sentence)

Obs: %[ n]s indicates that the read delimiter is line break and no longer the default is the space character. With this we avoid the problem of entering with a sentence and the scanf not read the whole sentence stopping in the first space. With this scanf will read the sentence until you find a enter ( n)

scanf needs to receive the memory address of a variable to put what was typed by the user.

So most of the time we just use the name of the vector to indicate that the address of its first position will be placed inside a pointer variable. Thus the code p = &vetorA[0] is the same as p = vector A ( are equivalent because, as I said, the name of a vector in the language C is synonymous with &vector[0].

*(p+0) = 10; Desrefenciamento: that is, allot the value 10 in the position of memory contained in p + the displacement (zero)

De-referencing: read the value of the referenced memory position ("pointed") by the pointer.

*(p+1) = 11; Dereferential: that is, allot the value 11 in the memory position contained in p + 1 displacement (a)

Doubt: What does *(p+1)?

To access the contents of N elements we use *(p+N)

It means that when the code is executed the address with you in p plus the offset of the pointer type size will be shifted.

A pointer pointing to the memory address of the first byte of a variable and knowing the type of the variable it will be able to traverse this variable.

Ex: if integer (int) has 4 bytes in a given system the pointer can traverse an integer vector by moving 4 in 4 bytes. And that’s how it really happens! So it’s important to declare the type of pointer so that it knows how to traverse the memory region it points to. Even void pointers need to be started with some literal(rvalue) so you know what kind of data it points to.

NOW ARRIVING AT THE ANSWER TO YOUR FIRST QUESTION:

STRUCTS AND POINTERS

A struct is a collection of one or more variables, possibly of different types, that can be manipulated together or separately To reference one of the internal fields within a structure type variable, the operator . (point) as follows: .field ex:

#include <stdio.h>
#include <locale.h> /*para acentuação*/
#include <stdlib.h>

typedef struct {
    int matricula;
    char nome[80];
    float notas[3];

}Aluno;
/* Acima declarei um modelo para uma estrutura(struct) e criei um alias(apelido) para este modelo
de nome Aluno. Desta forma ao solcitar a criação de uma variável do tipo desta estrutura
pode usar apenas: Aluno nome_da_variavel_struct; */

int main(int argc, char** argv)
{
    int i;
    system("CLS"); //limpa tela no windows
    setlocale(LC_ALL, "");//acenturar a saída
    Aluno *ponteiro_struct;
    //acima indica que ponteiro_struct é uma variável que vai armazenar o endereço de memória
    //do primeiro byte da struct. Lembre-se que ponteiro só armazena um único endereço de memória
    //Como o ponteiro_struct é do tipo Aluno ele saberá percorrer esta struct

    ponteiro_struct = (Aluno*)malloc(sizeof(Aluno));
    //a função malloc aloca bytes na memória e retorna o endereço do primeiro byte desta região 
    //alocada. Contudo ele informa que esta região não possui um tipo. Ela é void*. Assim é necessário
    //informar que tipo de dado você deseja que este região de memória recebem alocada seja estruturada
    //Usamos para isso a conversão explicita(cast) para indicar que queremos que a região de memória 
    //alocada por malloc seja estrutura da forma da struct Aluno. O ponteiro_struct agora
    //saberá percorrer e manipular esta região.

    printf("\nDigite a Matrícula: "); scanf("%d", &(*ponteiro_struct).matricula);
    /* lembre-se que para acessar campos de uma struct usamos a notação ponto.
    <variável>.<campo>. A variável neste caso é um ponteiro e para acessar a região de memória
    que ele aponta precisamos realiazar um desreferenciamento. Com isso fazermos (*ponteiro_struct)
    isso indica que queremos ir até a região apontada por ponteiro_struct. Uma vez lá
    usamos a notação ponto para acessar os campos da struct. (*ponteiro_struct).matricula */

    printf("\nMatricula Digitada: %d", ponteiro_struct->matricula);
    fflush(stdin); //limpa o buffer do teclado e evita ler o enter e pular o próximo scanf

                   //mais uma vez temos a possibilidade de usar um sinônimo em C
                   //(*ponteiro) pode ser substituido por ponteiro-> veja abaixo:

    printf("\nDigite a Matrícula: "); scanf("%d", &ponteiro_struct->matricula);

    /* Esta notação p-> é mais nítida e um pouco menos carregada que a notação (*p). Além de ser
    mais intuitiva. Pode-se acessar um membro de um tipo de dado estrutura de dado
    usando ponteiro de duas formas:
    Usando um desreferenciador entre parênteses e um ponto (.) para indicar o membro.
    Usando o próprio ponteiro e uma seta (->) para indicar o membro.*/

    printf("\nMatricula Digitada: %d", ponteiro_struct->matricula);
    fflush(stdin);

    printf("\nDigite nome do aluno: "); scanf("%[^\n]s", ponteiro_struct->nome);
    //string não precisa de & pois nome é o mesmo que &nome[0]
    printf("\nNome digitado: %s", ponteiro_struct->nome);

    for (i = 0; i < 3; i++) {
        printf("\nDigite a nota %d do aluno: ", i + 1);
        scanf("%f", &ponteiro_struct->notas[i]);
        // seria o mesmo que scanf("%d", (*ponteiro_struct).notas[j]);
        printf("\n----Nota %d do aluno = %.2f: ", i + 1, ponteiro_struct->notas[i]);
    }


    return 0;
}

STRUCT VECTORS AND POINTERS

Now let’s use the same code above only now with an array of struct and structs nested as similar in your code:

#include <stdio.h>
#include <locale.h> /*para acentuação*/
#include <stdlib.h>

typedef struct {
    int matricula;
    char nome[80];
    float notas[3];

}Aluno;


int main(int argc, char** argv)
{
    int i, j;
    system("CLS");
    setlocale(LC_ALL, "");
    Aluno *ponteiro_struct;

    ponteiro_struct = (Aluno*)malloc(3 * sizeof(Aluno));
    //Agora serão alocados o espaço de 3 structs do tipo Aluno e o o endereço do primeiro
    //byte desta região será colocada em ponteiro_struct

    for (i = 0; i < 3; i++) {
        printf("\nDigite a Matrícula: "); scanf("%d", &(*(ponteiro_struct + i)).matricula);

        printf("\nMatricula Digitada: %d", ponteiro_struct[i].matricula);
        fflush(stdin); //limpa o buffer do teclado e evita ler o enter e pular o próximo scanf

                       //mais uma vez temos a possibilidade de usar um sinônimo em C
                       //&(*(ponteiro_struct +i)).matricula) pode ser substituido por ponteiro[i] &ponteiro_struct[i].matricula veja abaixo:

        printf("\nDigite a Matrícula: "); scanf("%d", &ponteiro_struct[i].matricula);

        /* Esta notação p-> é mais nítida e um pouco menos carregada que a notação (*p). Além de ser
        mais intuitiva. Pode-se acessar um membro de um tipo de dado estrutura de dado
        usando ponteiro de duas formas:
        Usando um desreferenciador entre parênteses e um ponto (.) para indicar o membro.
        Usando o próprio ponteiro e uma seta (->) para indicar o membro.*/

        printf("\nMatricula Digitada: %d", ponteiro_struct[i].matricula);
        fflush(stdin);

        printf("\nDigite nome do aluno: "); scanf("%[^\n]s", ponteiro_struct[i].nome);
        //string não precisa de & pois nome é o mesmo que &nome[0]
        printf("\nNome digitado: %s", ponteiro_struct[i].nome);

        for (i = 0; j < 3; j++) {
            printf("\nDigite a nota %d do aluno %d: ", j + 1, i + 1);
            scanf("%f", &ponteiro_struct[i].notas[j]);
            // seria o mesmo que scanf("%d", (*ponteiro_struct).notas[j]);
            printf("\n----Nota %d do aluno %d = %.2f: ", j + 1, i + 1, ponteiro_struct[i].notas[j]);

        } //fim for

    }//fim for      

    return 0;
}

To show how to use the arrow symbol twice -> see now the data entry using the nested struct. See that me my code for simplifies I do not use alias(typedef).

Note the function: int Uncool(struct queue *queue) who uses queue->start->Prox

#include <stdio.h>
#include <stdlib.h>
//Declaração de tipos para a fila
struct no
{
    int valor;
    struct no *prox;
};

struct fila
{
    struct no *inicio, *fim;
    int tamanho;
};


// Funções basicas para a manipulação de filas

void IniciaFila(struct fila *fila)
{
    fila->inicio = NULL;
    fila->fim = NULL;
    fila->tamanho = 0;
    printf("Fila criada com sucesso");
}

int Vazia(struct fila *fila)
{
    return (fila->inicio == NULL);
}

void Enfileira(int x, struct fila *fila)
{
    struct no *aux;
    aux = (struct no *) malloc(sizeof(struct no));
    aux->valor = x;
    aux->prox = NULL;
    if (Vazia(fila)) {
        fila->inicio = aux;
        fila->fim = aux;
    }
    else {
        fila->fim->prox = aux;
        fila->fim = aux;
    }
    fila->tamanho++;
}

int Desenfileira(struct fila *fila)
{
    struct no *aux; int valor;
    if (Vazia(fila))
    {
        printf("Fila esta vazia\n");
        return 0;
    }
    aux = fila->inicio;
    valor = fila->inicio->valor;
    fila->inicio = fila->inicio->prox;
    free(aux);
    fila->tamanho--;
    return valor;
}

int Tamanho(struct fila *fila)
{
    return (fila->tamanho);
}

void mostrarFila(struct fila *fila)
{
    struct no *aux;
    aux = fila->inicio->prox;
    while (aux != NULL)
    {
        printf("%i\n", aux->valor);
        aux = aux->prox;
    }
}

// Programa Principal

int main(int argc, char *argv[])
{
    struct fila *fila;
    int i, numero;


    fila = (struct fila *) malloc(sizeof(struct fila));
    IniciaFila(fila);

    /*Insere cada chave na lista */
    for (i = 0; i<5; i++)
    {
        printf("Leitura do valor (%d) :", i);
        scanf("%d", &numero);
        Enfileira(numero, fila);
        printf("Enfileirou: %d \n", numero);
    }
    printf("Tamanho da fila: %d\n", Tamanho(fila));

    /*Desenfieleira cada chave */
    for (i = 0; i<5; i++)
    {
        numero = Desenfileira(fila);
        printf("Desenfileirou: %d \n", numero);
    }
    printf("Tamanho da fila: %d\n", Tamanho(fila));

    system("PAUSE");
    return 0;
}

Now it’s up to you. Use this knowledge and try to see the error of your code!

4


1) Because I don’t need to put (->) before speed, for example ( p->attributes->speed) in printf(" tVelocity: %d n n", p->speed attributes.) ?

atributos is a structure and not a pointer so has to be .

To be with -> would have to have been defined as pointer, like this:

typedef struct{
    ...
    Atributos *atributos;
    ...
}Pokemon;

And in this way it would also be necessary every time one creates a Pokemon with malloc or realloc, also allocate space for the field atributos likewise.

2) If 'p' is a dynamically allocated vector, why do I need to put "&" in imprime_pokemon_name(&p[indice_pokemon]) and imprime_pokemon_atak(&p[indice_pokemon].attacks[indice_attack]) ? Normally I define my functions with vectors, in function int main, without the '&', as, func(vector)

The function imprime_pokemon_nome receives a pointer, as we can see in your signature:

void imprime_pokemon_nome(Pokemon *p) {

And when we do p[indice_pokemon] we get the structure Pokemon and not the pointer. To get the pointer, we have to get the address through the operator &. Making a small analogy for integers is easy to see that it makes sense. Let’s imagine that we have:

int numeros[3] = {1,2,3};

When do we do numeros[2] we get an integer and not the pointer. To get the pointer for that element we would have to do &numeros[2]

3) The void function imprime_pokemon_atak(Pokemon *p) is in trouble struct, but I have no idea what it is.

Exactly, the function is waiting to receive a pointer to Pokemon:

void imprime_pokemon_atak(Pokemon *p){

And a pointer is being passed to a Ataques:

&p[indice_pokemon].ataques[indice_ataque]

To fix the function for this problem we can change the function imprime_pokemon_atak to get the right kind:

void imprime_pokemon_atak(Ataques *a){ //recebe agora um Ataques*

    //como agora recebe o ponteiro para o ataque o acesso é mais simples
    printf("Nome do Ataque: %s\n", a->nome_ataque); 
    printf("Poder base: %d\n", a->poder_base);
    printf("Acuracia: %f\n", a->acuracia);
    printf("Classe: %c\n\n", a->classe);
}

Additional notes:

Before while read the option that then repeats at the end of the while:

scanf("%d", &option);

while(option != 0){

    ...
    scanf("%d", &option);
}

Better turn this logic into:

int option = 1, i = 0, indice_pokemon, indice_ataque; //agora começa com 1 para entrar no while
...
while(option != 0){
    printf("\nInsira a opção");
    scanf("%d", &option);
    ...
    //resto do código
}

I also advise you to put printf of each of the options to be easy to understand which values are being inserted

2

Good morning,

I believe these doubts of yours exist because you haven’t mastered pointers and struct yet.. I will try to answer in a simple and easy way according to what I know.

1) Because I don’t need to put (->) before speed, for example ( p->attributes->speed) in printf(" tVelocity: %d n n", p->speed attributes.) ?

Note that P is a pointer that points to the struct, so to access the content of the "target" of that pointer you use the "->" and struct, as it is only one thing in memory you access with the "." when you get POO, you’ll understand this better.

2) If 'p' is a dynamically allocated vector, why do I need to put "&" in imprime_pokemon_name(&p[indice_pokemon]) and imprime_pokemon_atak(&p[indice_pokemon].attacks[indice_attack]) ? Normally I define my functions with vectors, in function int main, without the '&', as, func(vector).

This "&" indexer means content, as a vector is an array of pointers, you access the content of it to be able to manipulate.

and on the third question, could post what the compilation error? thank you.

I hope I helped, I’m not giving you the most technical explanation, but the one that for me is the easiest to understand.. I believe you are a student, so I advise you to make the most of pointers with your data structure teacher.

Browser other questions tagged

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