Doubt about C pointers

Asked

Viewed 167 times

5

So guys, I got a question here.

What does each of these statements mean below?

*ponteiro = Valor do ponteiro

 ponteiro = (?)

&ponteiro = Endereço de memória do ponteiro

I would like someone to explain to me the use of the middle, only the "pointer" without the '*' and '&'

  • 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.

3 answers

4

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:

  1. if it is a variable and you use the &
  2. 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);
  }
}
}

1

The operator * when used in the declaration of a variable, indicates that that variable is a pointer:

// Numa arquitetura 32-bit, sizeof(ptrToShort) trará como resultado 4,
// embora o tipo short tenha tamanho 2, porque ptrToShort é um "ponteiro"
// para uma variável do tipo short, ou seja, ele armazenará o endereço da
// memória onde estará armazenado um valor do tipo short.
short *ptrToShort;

// Mesma coisa, sizeof(ptrToDouble) trará como resultado 4,
// num sistema 32-bit, embora o tipo double tenha tamanho 8.
double *ptrToDouble;

Pointers point to an address in memory, where the value of some variable is stored, for example ptrToShort == 0x053DC88C. In that case, 0x053DC88C is a valid memory address, and must be the start address of the space allocated in memory for a variable of type short. Like the guy short occupies 2 bytes, this variable short would be using the address memory 0x053DC88C at the 0x053DC88D (0x053DC88C + 1).

But when the operator * is used next to a variable in a common expression, other than a variable declaration, means that the program will be accessing the value stored in the memory address to which the pointer is pointing.

And the operator & returns the address in the memory of a variable, so it is used to load a value to a pointer. It is through the operator & that we obtain the address of a variable, in order to be able to store it in a pointer.

So:

short inteiro = 123;
short outroInteiro = 0;
short *ptrToShort = NULL;

// A variável ptrToShort é um ponteiro para tipos short, então ela armazenará
// um endereço da memória, que esteja armazenando um valor do tipo short.
// O operador & retorna o endereço na memória de uma variável, então aqui
// estamos dizendo que ptrToShort agora aponta para o endereço na memória
// da variável inteiro.
// Vamos supor que a variável inteiro esteja alocada no endereço 0x053DC88C
// da memória. A expressão &inteiro irá retornar o valor 0x053DC88C, e a
// variável ptrToShort passará a conter o valor 0x053DC88C, após a linha
// abaixo.
ptrToShort = &inteiro;

// O operador * permite acessar o valor armazenado no endereço apontado
// por um ponteiro, então, o comando abaixo está dizendo: "Pegue o valor
// do tipo short, armazenado no endereço apontado por ptrToShort, e jogue
// na variável outroInteiro". O comando abaixo é equivalente a
// outroInteiro=inteiro, já que *ptrToShort==inteiro.
// Após o comando abaixo outroInteiro terá o valor 123.
outroInteiro = *ptrToShort;

// O operador * permite acessar e manipular o valor armazenado no endereço
// apontado por um ponteiro. Como o ponteiro ptrToShort aponta para o endereço
// da variável inteiro, o comando abaixo é o mesmo que inteiro=321, logo, 
// após o comando abaixo, a variável inteiro terá o seu valor alterado de 123
// para 321, já que *ptrToShort==inteiro.
*ptrToShort = 321;

On the third point of your doubt:

&ponteiro = Endereço de memória do ponteiro

this is not a valid command because &ponteiro is a r-value, and therefore cannot appear on the left side of a value assignment. See more about this here:

lvalue and rvalue in C language
https://www.geeksforgeeks.org/lvalue-and-rvalue-in-c-language/

C address of an address of a variable
https://stackoverflow.com/a/9976629/8133067

And finally, answering your main question:

 ponteiro = (?)

I would like someone to explain to me the use of the middle, only the "pointer" without the '*' and '&'

Taking into account that this variable ponteiro is actually a pointer, that is, it has been declared as data_type *ponteiro;, if you throw such a value directly into it, it is expected that this value is a valid address in memory, such as that address example 0x053DC88C:

 ponteiro = 0x053DC88C;

If you play some value that is not a valid address in memory, when trying to use this pointer you will probably get a memory access error, or, if you play a random value, which by coincidence is a valid address in memory, your pointer will be pointing to a random address in memory, which may be storing any information.

0

I believe the other statements are incorrect. Not being a variable statement:

  • *ponteiro would mean access to the first memory address, where the value is stored;

  • ponteiro would mean the value stored in memory;

  • and &ponteiro create a pointer to the existing pointer.

In the case of variable declaration:

  • *ponteiro would allocate memory to store a pointer pointing to null;

  • ponteiro would allocate memory to store variable;

  • and &ponteiro would be an invalid instruction.

Browser other questions tagged

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