Mas por que vemos tanto falarem sobre isso, se não existe?
Na verdade, o que existe é uma espécie de 'truque', que é passar o endereço de memória, através de ponteiros, para funções, simulando uma passagem por referência.
Então não há problema em falar de passagem por referência em C, apenas use seu bom senso.
- Baixe o conteúdo do site: Apostila de C
Como alterar uma struct em uma função
Ok, ler os dados de uma struct através de funções é bem
simples e é feito exatamente da mesma maneira que fizemos com outros tipos de
dados.
Porém, só ler nem sempre é tão útil assim. E quando
quisermos alterar uma struct?
Vamos ter que alterar na main() ?
Claro que não! Já explicamos que entupir a função main() é um péssimo hábito de
programação. Temos que dividir bem as tarefas de um aplicativo em C em diversas
funções, cada uma fazendo uma tarefa bem específica.
Se você lembrar bem, para alterar o valor de uma
variável, fizemos uso da passagem por referência, onde enviamos o endereço de
memória de uma variável, em vez de uma cópia de seu valor (como é feito na
passagem por valor).
Para fazer, a função deve ser declarada de modo a esperar
receber, como argumento, um endereço de memória. Ou seja, o parâmetro é um
ponteiro. A nossa função preenche seria:
void Preenche(CARRO *car);
E para passarmos a struct ? Temos que passar o endereço
de memória dessa estrutura, e isso é feito colocando o operador & antes do
nome da struct:
Preenche(&fusca);
Até aí, tudo ok.
O problema vem quando vamos tratar a estrutura dentro da
função, pois devemos ter um cuidado especial e é aqui onde os iniciantes
costumam se confundir e se complicar.
A função vai receber um ponteiro car de uma estrutura.
Para alterarmos os membros da estrutura, temos que usar o
asterisco antes do ponteiro, lembra? O ponteiro em si é só um endereço de
memória, o valor para qual esse ponteiro
aponta é obtido quando colocamos um asterisco antes do nome do ponteiro.
Por exemplo, para preenchermos o campo modelo:
gets( (*car).modelo);
E para acessar o elemento que armazena a potência do
motor?
(*car).potenciaMotor
E para alterar esse valor, dentro da função scanf(), por exemplo?
scanf("%f", &(*car).potenciaMotor);
Então o código de nossa função que recebe e altera os
membros de uma struct fica:
void Preenche(CARRO *car) { printf("Modelo do carro: "); gets( (*car).modelo ); printf("Motor: "); scanf("%f", &(*car).potenciaMotor); printf("Ano: "); scanf("%d", &(*car).anoFabricacao); printf("Numero de portas: "); scanf("%d", &(*car).numPortas); }
O operador ->
Embora tenhamos explicado passo por passo como chegar na
função anterior, temos que assumir que fica um pouco confuso e complexo nosso
código:
&(*car).numPortas
Note também que colocamos o asterisco e o ponteiro em
parêntesis separado.
Consegue imaginar o motivo disso?
Vamos imaginar que em vez de: (*car).numPortas
Usássemos: *car.numPortas
Podemos interpretar isso de duas maneiras:
1. Estamos
tentando acessar o elemento ‘numPortas’, que é um membro da struct que é apontada pelo ponteiro ‘car’.
Que é o que vínhamos fazendo, usando parêntesis.
2. Estamos
tentando acessar o ponteiro ‘numPortas’, da estrutura ‘car’.
Mas ‘car’ não é uma struct, muito menos tem o membro ‘numPortas’.
Sabemos que ‘car’ é apenas um ponteiro pra uma struct, ou
seja, ele é um endereço de memória. E é isso o que acontece quando não
colocamos os parêntesis, pois para o C, o ponto final (.) tem procedência maior
que o asterisco (*).
Para evitar problemas, usamos o parêntesis em volta do
ponteiro ‘car’.
Mas ainda assim fica confuso, e em programação C, ficar
confuso não é legal. Por isso, criaram um operador que vai facilitar isso, o
->
-> é simplesmente um atalho que substitui o asterisco
e os parêntesis (é fácil esquecer e se confundir com estes), e com o ‘->’ a
coisa se torna bem óbvia.
Por exemplo, em vez de: *(ponteiro).
Escrevemos: ponteiro->
No nosso exemplo do carro, as duas linhas seguintes são
equivalentes:
scanf("%d", &(*car).anoFabricacao);
scanf("%d", &car->anoFabricacao);
Fica bem fácil de entender o: car -> anoFabricação
Exemplo: Alterando e exibindo structs através de funções
em C
Crie um programa na linguagem C que define a estrutura de
um carro, altere seus dados através de uma função (use passagem por referência
e o operador ->) bem como use outra
função para exibir os membros da struct.
Nosso código, agora fazendo uso do operador -> , será:
#include <stdio.h> // Curso C Progressivo: www.cprogessivo.net // O melhor curso de C! Online e gratuito ! // Apostila online, tutorial completo sobre // a linguagem de programação C ! typedef struct { char modelo[30]; float potenciaMotor; int anoFabricacao, numPortas; }CARRO; void Exibe(CARRO car) { printf("\n\tExibindo carro\n"); printf("Modelo: %s\n", car.modelo); printf("Motor: %.1f\n", car.potenciaMotor); printf("Ano: %dn", car.anoFabricacao); printf("%d portas\n", car.numPortas); } void Preenche(CARRO *car) { printf("Modelo do carro: "); gets( car->modelo ); printf("Motor: "); scanf("%f", &car->potenciaMotor); printf("Ano: "); scanf("%d", &car->anoFabricacao); printf("Numero de portas: "); scanf("%d", &car->numPortas); } int main(void) { CARRO fusca; Preenche(&fusca); Exibe(fusca); return 0; }
Bem mais fácil e óbvio de entender, não acham?
É importante entender bem o conceito do operador -> e
da passagem de estruturas para funções, pois faremos uso desses conhecimentos
mais a frente em nossa apostila de C, na seção de “Estrutura de dados III:
Listas, Filhas e Pilhas”.
12 comentários:
Cara você utiliza o gcc como compilador ? Pois pelo que eu saiba o gets não pertence mais a biblioteca padrão e deve se utilizar fgets em vez do gets. Pelo menos é o que vi em alguns sites, como esses:
http://gcc.gnu.org/ml/gcc-help/1999-10n/msg00077.html
Olá Unknown,
Cara, essas coisas de 'função perigosa', 'não indicada', 'uso errado', 'uso não recomendado' etc, é complicado.
Se formos ser rigorosos, vamos acabar programando da maneira como se programa em C pro Kernel do Linux, ou seja, de uma maneira bem otimizada e altamente rígida.
Nosso objetivo aqui no curso é ensinar iniciantes, para quem está começando mesmo a aprender.
Creio que se formos ensinar todos esses detalhes e pormenores, vai acabar ficando complicado e desestimulante.
Mas informação nunca é demais, por isso agradecemos e vamos deixar seu comentário aí.
Sinta-se a vontade para nos dar outras dicas como esse e sua opinião.
Amigo o código:
void transfereTempPos( Pilha **pPos, Pilha **pTemp, Pilha *aux )
aux = (*(*pTemp)).prox; //(*p).x = 7.0; ou p->x = 7.0;
(*(*pTemp)).prox = *pPos;
*pPos = *pTemp;
*pTemp = aux;
Poderia me explicar o uso de dois asterisco **pPos e (*(pTemp)).prox = *Pos?
Ta meio obscuro isso para mim ainda.
Obrigado
Olá,
Muito bom sua apostila. Observei ali que uma função recebe por referencia uma estrutura. Como seria se esta função recebesse por referencia uma estrutura vetor ?
forte a todos.
obrigado.
Olá Anônimo,
O que você quer dizer com 'estrutura vetor' ? Não entendi bem.
eu nao entendi bem porque usar
&(*car).anoFabricacao, tipo, o asterisco serve pra dizer que queremos o valor, e o "&", pra dizer que queremos o endereço, entao oque voce fez foi pegar o valor do ponteiro car, e depois pegar o endereço do valor do ponteiro. Nao da pra simplesmente colocar car.anoFabricacao, sem o "&" nem o "*"?
lixo virtual,
Ao usar (*car).anoFabricacao na função printf("Ano de Fabricaco: %d, (*car).anoFabricacao); , estamos mostrando o valor do campo anoFabricacao da struct CARRO.
Usando &(*car).anoFabricacao na funcao scanf("%d",&(*car).anoFabricacao); , estamos armazenando um valor que o usuário informou no campo anoFabricaco da struct CARRO.
Não sei se ficou claro, mas é isso.
Tambem estou com a duvida de um amigo acima que não foi respondido porque sua pergunta não foi entendida, mas é o seguinte, no caso do exemplo de criação de carro temos a struct:
typedef struct
{
char modelo[30];
int ano, poras;
}CARRO;
daí se o enunciado pedisse pra cadastrar mais de um carro
CARRO cars[3];
quando eu fosse preencher o modelo do primeiro carro por exemplo na função, como ficaria?
Eu tentei
fgets(cars[cont]->modelo,30,stdin);
porem deu errado,
mudei pra "gets" e tambem deu errado
apresentando o erro "invalid type argument '->'".
Como seria a forma correta?
Guilherme0890, nessa situação como se trata de um vetor ele próprio já é passado por referência, daí não precisa usar "*" nem "->" para alterar o valor do campo no array. A passagem para a função é feita de forma direta, apenas colocando o tamanho do array.
Ex:void ler(carro novo[2]){
int cont;
for(cont=0;cont<MAX;cont++){
fflush(stdin);
printf("\nDigite os dados do veiculo %d\n",cont+1);
printf("Digite o modelo do carro: ");
gets(novo[cont].modelo);
printf("Digite a cor do carro: ");
gets(novo[cont].cor);
....
dentro da main
... ler(novo);
Por favor, alguem me de uma ajuda de como passar por referencia VETORES de STRUCT. Não estou conseguindo de jeito nenhum.
Autor, então como devo prosseguir com alocação dinamica? Veja:
CARRO *fusca = (CARRO*)calloc(10, sizeof(CARRO));
strcpy( (*fusca[2]).modelo , "Gol" );
Esse formato não deveria funcionar também de acordo com o texto?
E seu eu passar um vetor do tipo 'carro'como argumento para uma função? Como ficaria? Por exemplo:
CARRO car[5];
Como passaria isso como argumento? E como a função seria declarada de modo a receber?
Postar um comentário