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:
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!
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.
– user3629249
By calling any of the function families
scanf ()
, always check the returned value (not parameter values) to ensure the operation has been successful– user3629249
By calling
scanf ()
with a "%s" specifier, always include a modifier that is 1 less than the input buffer length to avoid buffer overflow.– user3629249