There is a little confusion among operators here. But first, I need to talk about what is a variable.
According to the author of the book Concepts of Programming Lingaugens Robert Sebesta, a variable is a memory region that has type and name. Simple like this.
Then, the dereferencing operator &
is only applicable to variables. The result of this is not the address of the pointer, but the address (a number pointing to the memory region) of the variable. In the case, &ponteiro
is the address where the variable is ponteiro
.
The reference operator *
however applies to values. This operator will rescue what is in the memory address of the variable. Then, in case there is a variable ponteiro
pointing to the first box of the vector {42,2,10}
, *ponteiro
would return 42
.
That said, now I can stay in your question:
I would like someone to explain to me the use of the middle, only the "pointer" without the '*' and '&'
This more formally can be understood as:
I would like someone to explain to me the use of references, other than in the case of using the dereferencing operators &
or that of reference *
Normally you need this when using shared structures. In this case, the structure exists and you only have the reference for it. You can get this reference in two ways:
- if it is a variable and you use the
&
- if it is a dynamically allocated memory region
In general, you only have the difference between these two forms in the creation (said above) and when disposing of the data, in which dynamic memory needs a free
explicit to release the memory back. But no need to worry further, the general manipulation of the data is the same, regardless of the origin of the reference.
So, say you have a tree used to store words. Let’s say that it is a binary tree, in which each point of the tree has a parent point and up to two child points (called creatively esquerdo
and direito
). The structure of this tree is something like this then:
struct arvore {
char *conteudo;
struct arvore *esquerdo;
struct arvore *direito;
};
When you add a word to the tree, you will make a call like this:
struct arvore* inserir_palavra(struct arvore *raiz, char *palavra);
At some point, you will need to define if the word goes to the knot you will live in raiz->esquerdo
or raiz->direito
. If it is a search tree, it is a matter of lexicographic order. Then you will need to do something like this:
int cmp_palavra = strcmp(palavra, raiz->conteudo);
if (cmp_palavra == 0) {
// a palavra já existe, pode desencanar
return raiz;
} else if (cmp_palavra < 0) {
// palavra vem antes, portanto a esquerda
// se não tiver nada a esquerda, vou inserir o novo ponto na esquerda
if (raiz->esquerdo == NULL) {
struct arvore* nodo_palavra = cria_nodo(palavra);
raiz->esquerdo = nodo_palavra;
return nodo_palavra;
} else {
// bem, não está no ponto atual e o ponto de destino está ocupado, então vou descer nesse destino
return inserir_palavra(raiz->esquerdo, palavra);
}
} else {
// não é igual nem é anterior, então é posterior, está à direita
// se não tiver nada a direita, vou inserir o novo ponto na direita
if (raiz->direito == NULL) {
struct arvore* nodo_palavra = cria_nodo(palavra);
raiz-> direito = nodo_palavra;
return nodo_palavra;
} else {
// bem, não está no ponto atual e o ponto de destino está ocupado, então vou descer nesse destino
return inserir_palavra(raiz-> direito, palavra);
}
}
Did you see how I’m using only the pointer itself when navigating the tree? I only interested in them the reference, I did not need to navigate within the content of the structure for this.
The case here of pointers direito
and esquerdo
illustrate what you would like to know when to use a pointer without the referencing and de-referencing operators.
The most complete code of the example insertion operation above follows after this paragraph. To get rid of the tree call the function liberar_arvore
. Note that I am making code intrinsically single thread, so don’t blame me if you try to run a program with parallel operations and things start to fail.
struct arvore {
char *conteudo;
struct arvore *esquerdo;
struct arvore *direito;
};
char* copia_char_dinamico(char *original) {
char *copia = calloc(strlen(original) +1, sizeof(char));
return strcpy(copia, original);
}
struct arvore* cria_nodo(char *palavra) {
// o calloc já cria com todos os bytes zerados, não tenho então preocupação com estar esquerdo e direito para NULL
struct arvore *novo_nodo = calloc(1, sizeof(struct arvore));
novo_nodo->conteudo = copia_char_dinamico(palavra);
return novo_nodo;
}
struct arvore* inserir_palavra(struct arvore *raiz, char *palavra) {
if (raiz == NULL) {
return cria_nodo(palavra);
}
int cmp_palavra = strcmp(palavra, raiz->conteudo);
if (cmp_palavra == 0) {
// a palavra já existe, pode desencanar
return raiz;
} else if (cmp_palavra < 0) {
// palavra vem antes, portanto a esquerda
// se não tiver nada a esquerda, vou inserir o novo ponto na esquerda
if (raiz->esquerdo == NULL) {
struct arvore* nodo_palavra = cria_nodo(palavra);
raiz->esquerdo = nodo_palavra;
return nodo_palavra;
} else {
// bem, não está no ponto atual e o ponto de destino está ocupado, então vou descer nesse destino
return inserir_palavra(raiz->esquerdo, palavra);
}
} else {
// não é igual nem é anterior, então é posterior, está à direita
// se não tiver nada a direita, vou inserir o novo ponto na direita
if (raiz->direito == NULL) {
struct arvore* nodo_palavra = cria_nodo(palavra);
raiz-> direito = nodo_palavra;
return nodo_palavra;
} else {
// bem, não está no ponto atual e o ponto de destino está ocupado, então vou descer nesse destino
return inserir_palavra(raiz-> direito, palavra);
}
}
}
The answers given explain the pointer concept but it would be interesting if you put some context in your question to direct a specific answer to your question, such as the code snippet where you saw these parts and got doubt.
– Pagotti