Neste artigo serão discutidos alguns tópicos sobre ponteiros e estruturas. Na linguagem C, uma estrutura é uma coleção de variáveis referenciada por um nome, também chamada de tipo de dado agregado. Utilizando ponteiros para estruturas a forma como os membros são referenciados é alterada, sendo esse tópico importante para entender a passagem de estruturas para funções.
Declaração de Ponteiros para Estruturas
Um ponteiro para estrutura pode ser declarado da forma mostrada abaixo.
struct <tipo> * <nome do ponteiro>;
Cabe observar que a declaração do ponteiro para estruturas é exatamente igual à declaração de ponteiros para outros tipos de variáveis. Para evitar erros de compilação do tipo da estrutura deve ser previamente definido. Considere o exemplo abaixo.
struct NumeroComplexo
{
float Real;
float Imaginario;
};
A declaração de um ponteiro para estrutura NumeroComplexo é mostrada abaixo. Essa forma de declaração será equivalente para ponteiros de estruturas que são declarados como parâmetros de funções.
struct NumeroComplexo * ptNC;
Para inicializar o ponteiro com um endereço válido é necessário utilizar o operador ‘&’ antes de uma estrutura. O código abaixo mostra a declaração de uma estrutura e a inicialização do ponteiro.
struct NumeroComplexo Num1; ptNC = &Num1;
Referenciando Elementos da Estrutura
Para acessar os membros da estrutura o operador seta ‘->’ deve ser utilizado. Esse operador é utilizado no lugar do operador ponto e determina que o acesso ao membro será realizado por um ponteiro [1]. Abaixo é mostrado o acesso ao membro Real que foi definido na estrutura NumeroComplexo.
ptNC->Real
Ao utilizar o operador seta estamos acessando o conteúdo do membro da estrutura, no entanto é possível obter o endereço individual de cada variável declarada na estrutura. Neste caso, o operador ‘&’ deve ser utilizado antes do nome da estrutura. O código abaixo mostra como obter o endereço de um membro a partir da estrutura ou de um ponteiro para estrutura.
&Num1.Real &ptNC->Real
A Figura 1 ilustra essas operações. Note que acessar o membro e depois obter o endereço é o mesmo que obter o endereço diretamente da estrutura.
Obter o endereço de um membro específico pode ser interessante em algumas situações, por exemplo, passar apenas alguns valores de uma estrutura para uma determinada função. Considere o exemplo abaixo no qual um ponteiro para float é utilizado para acessar o membro Imaginario da estrutura NumeroComplexo.
float * pt = &Num1.Imaginario;
Essa situação é ilustrada na Figura 2.
Passando Estruturas para Funções
Uma das vantagens de poder criar um ponteiro para função se dá pelo fato de utilizar um ponteiro como parâmetro de função, isto é, uma estrutura pode ser passada por referência para uma determinada função. Isso pode ser considerado um ganho de desempenho, pois ao passar uma estrutura por referência estaremos apenas manipulando o seu endereço, evitando que todos os seus membros sejam passados por valor. No exemplo abaixo foi criada uma função para somar dois números complexos, para isso a função recebe três estruturas por referência.
void NC_Adicao(struct NumeroComplexo * n1, struct NumeroComplexo * n2, struct NumeroComplexo * resultado);
Lembrando que o acesso deve ser realizado pelo operador seta.
void NC_Adicao(struct NumeroComplexo * n1, struct NumeroComplexo * n2, struct NumeroComplexo * resultado)
{
/*validar ponteiros*/
/*...*/
resultado->Real = n1->Real + n2->Real;
resultado->Imaginario = n1->Imaginario + n2->Imaginario;
}
A chamada da função é mostrada abaixo.
struct NumeroComplexo Num1; struct NumeroComplexo Num2; struct NumeroComplexo Resultado; Num1.Real = 7; Num1.Imaginario = 5; Num2.Real = 8; Num2.Imaginario = -3; NC_Adicao(&Num1, &Num2, &Resultado);
Note que apenas os endereços das estruturas são passados para a função. Para evitar que os parâmetros sejam alterados, o modificador const pode ser utilizado.
void NC_Adicao(struct NumeroComplexo const * const n1, struct NumeroComplexo const * const n2, struct NumeroComplexo * const resultado)
{
/*validar ponteiros*/
/*...*/
resultado->Real = n1->Real + n2->Real;
resultado->Imaginario = n1->Imaginario + n2->Imaginario;
}
O modificador const após o operador ‘*’ determina que o endereço que o ponteiro armazena não pode ser alterado, já o modificador antes do operador ‘*’ garante que o conteúdo deste endereço não pode ser alterado. Deste modo, somente o ponteiro resultado pode alterar o conteúdo do endereço apontado.
Conclusão
Os tópicos discutidos neste artigo são de grande importância para trabalhar com estruturas de dados. Como demonstrado, os ponteiros também podem ser utilizados para acessar os membros de uma estrutura, no entanto o mesmo pode ser realizado por ponteiros do tipo do membro uma vez que é possível obter seu endereço. Essa característica permite que a estruturas sejam passadas por referência na chamada de funções, evitando que todos os membros da estrutura sejam passados por valor. Isso também possibilita que os membros reais sejam alterados na estrutura usada na chamada da função.
Referências
[1] – Livro: C, completo e total – 3ª edição revista e atualizada. Herbert Schildt.Fonte da imagem destacada: https://listamaze.com/top-10-programming-languages-for-job-security/











Muito Bom!
Obrigado, Gustavo.
Essa série está ficando sensacional!
Olá, Igor.
Obrigado pelo retorno!