Buscar

Resumo_VetorDePonteiro

Esta é uma pré-visualização de arquivo. Entre para ver o arquivo original

Resumo Vetor de ponteiros: 
**********************************************************************************
OBS: Essa matéria cai na P2 e P3!
**********************************************************************************
- Definição:
É basicamente um vetor (Sim, um vetor comum) que armazena ponteiros para algo! Pode ser qualquer tipo de ponteiro (int, float), mas em 99% dos casos, são usados ponteiros para String ou para uma Struct.
Lembra que em Prog 1 a única forma de armazenar um conjunto de nomes (Strings) era através de uma Matriz? 
Exemplo: Vamos analisar o seguinte caso, precisamos armazenar um conjunto de 50 nomes com no máximo 30 caracteres:
- Usando Matriz (Prog 1): 
Antes de tudo precisava definir o Tamanho máximo da string. (Deixando sempre um espaço extra para o '\0' no final!)
#define TAM 31 
char nomes[50][TAM]; ou char nomes[50][31];
Agora vamos analisar o seguinte, desses 50 nomes, todos possuem 30 caracteres? 
NÃO! Estamos gastando memória à toa! É justamente por isso que >>NÃO<< compensa usar Matriz!
- Usando vetor de ponteiros: 
OBS: Uma string é representada pelo endereço do seu primeiro caracter, ou seja, um ponteiro para seu primeiro caracter. Como são vários nomes, podemos simplesmente armazenar todos num vetor de ponteiros para char!
char * nomes[50];
Mas se não especificamos o tamanho máximo da String, como o compilador vai adivinhar?
É justamente aqui que precisamos mais do que nunca de alocação dinâmica! (Malloc)
Pois cada nome será alocado dinamicamente com o seu tamanho >>Exato<<!
#define MAX 50
char * LeNome (){
	char * novo;
	char temp[31];
	puts("Digite o Nome: ");
	scanf(" %30[^\n]",temp);
	novo = (char *) malloc (strlen(temp) + 1);
	if(!novo) return NULL;
	strcpy (novo, temp);
	return novo;
}
********************************************************************************
OBS: Essa função lê um nome, armazena numa variável temporária Temp.( pois não sabemos o tamanho do nome!) Lembra que a função malloc precisa saber o tamanho para alocar a memória? Por isso usamos uma variável temporária!
Após alocar a memória com o tamanho exato do nome (strlen(temp) + 1) é só usar a função strcpy para fazer uma cópia do conteúdo e por fim, retornar o ponteiro para char.
*********************************************************************************
void imprimeNomes (char **nomes, int n){
	int i;
	for(i=0;i<n;i++){
	 printf("Nome: %s\n",nomes[i]);
	}
}
********************************************************************************
OBS: Percebe a forma como escrevi o parâmetro da função?
É possível escrever char *nomes[50] ou char **nomes! Ambas formas estão corretas!
Mas no parâmetro de uma função é o >>ÚNICO<< lugar onde podemos omitir o tamanho do vetor!
OBS2: Lembra como era feito em Prog 1? Exemplo: void imprime (int notas[]); 
A gente omitia o tamanho do vetor, mas uma outra forma de escrever esse parâmetro seria: int *notas 
Pois >>>TODO<<< vetor é representado pelo endereço de seu primeiro elemento! Ou seja, um ponteiro para o primeiro elemento do vetor! 
Então qualquer tipo de vetor pode ser escrito dessa forma, inclusive um vetor de ponteiros para algo! 
Resumindo: char * é um ponteiro para char! 
 char ** é um vetor de ponteiros para char!
 
********************************************************************************
void liberaNomes (char **nomes, int n){
	int i;
	for(i=0;i<n;i++){
		free(nomes[i]);
	}
}
********************************************************************************
OBS: Função que libera (Devolve a memória alocada) de todos os nomes, lembrando que a função free recebe como parâmetro um ponteiro para algo e é justamente o que temos em cada posição do vetor! 
********************************************************************************
int main (void){
	char *nomes[MAX];
	int i;
	for(i=0; i<MAX; i++){
	 nomes[i] = LeNome();
	 if(!nomes[i]){
	 liberaNomes(nomes, i-1);
		 exit(1);
	 }
	}
	imprimeNomes(nomes, MAX);
	liberaNomes(nomes, MAX);
	return 0;
}
 
OBS: Como nesse caso, já sabemos o tamanho máximo do nosso vetor, declaramos dessa forma: char *nomes[MAX];
Lembrando que MAX é uma macro representada pelo #define MAX 50 no começo do programa!
Cada posição do vetor recebe um ponteiro para char, retornado pela função LeNome();
OBS2: Se caso ocorrer algum erro na leitura (LeNome retornar NULL), não podemos simplesmente encerrar o programa com exit! Antes de tudo, precisamos liberar o que já foi alocado!!!
OBS3: Notou que tirando essa novidade dos asteriscos, a manipulação (Leitura, impressão, envio como parâmetro) de um vetor de ponteiro é exatamente o mesmo esquema que um vetor comum? =D 
------------------------------------//-------------------------------------------
Exemplo 2: Vetor de ponteiro para struct
Vamos criar um vetor de ponteiros para a estrutura Aluno, mas dessa vez não sabemos quantos alunos são. (O usuário que vai digitar a quantidade!)
typedef struct aluno{
	char nome[50];
	int matricula;
}Aluno;
Aluno * leAluno (){
	Aluno *novo;
	
	novo = (Aluno *) malloc (sizeof(Aluno));
	if(!novo) return NULL;
	puts("Nome e matricula:");
	scanf(" %30[^\n]%d",novo->nome, &novo->matricula);
	return novo;
}
*********************************************************************************
OBS: Cada posição do vetor (Criado na main) armazena um ponteiro para uma struct Aluno e é essa função que aloca dinamicamente um novo aluno e retorna para a main.
Dica importante: 
A partir de agora vou sempre omitir as mensagens de erro do malloc, pois são completamente opcionais! Os professores costumam sempre usar pra serem mais didáticos, mas numa prova ou exercício qualquer, quanto menos você precisar escrever melhor né? :)
- Antes: (Com mensagem de erro)
if(!novo){
 puts("Erro! Sem memoria!");
 return NULL;
}
- Depois: (Sem mensagem de erro)
if(!novo) return NULL;
OBS: Como é só um comando, é opcional usar chaves de bloco no if! Dessa forma, podemos colocar simplesmente ao lado, na mesma linha do if! Super prático, não? :)
*********************************************************************************
void exibeAluno (Aluno *a, int n){
	printf("Nome: %s\nMatricula: %d\n\n",a->nome, a->matricula);
}
OBS: Essa função exibe somente um aluno por vez! Por isso ela recebe como parâmetro Aluno *a e >>não<< Aluno ** a! E o 'for' pra rodar o vetor está na main.
void liberaAlunos (Aluno **alunos, int n){
	int i;
	for(i=0;i<n;i++){
		free(alunos[i]);
	}
	free(alunos);
}
OBS: Quase idêntica a função liberaNomes do primeiro exemplo, com exceção de um mini detalhe...Dessa vez o vetor que armazena os ponteiros também é alocado dinamicamente! Então não podemos simplesmente liberar ele, antes de tudo precisamos liberar a memória de >> CADA << ponteiro alocado dinamicamente!!! E >> somente << no final de tudo (Assim que terminar o 'for') que liberamos a memória do vetor!
int main (void){
	Aluno ** alunos;
	int i, n;
	puts("Quantos alunos?");
	scanf("%d",&n);
	alunos = (Aluno **) malloc (n * sizeof(Aluno *));
	if(!alunos) exit(1);
	for(i=0; i<n; i++){
		alunos[i] = leAluno();
		if(!alunos[i]){
			liberaAlunos(alunos, i-1);
			exit(2);
		}
	}
	for (i=0; i<n; i++){
		exibeAluno(alunos[i], n);
	}
	liberaAlunos(alunos, n);
	return 0;
}
OBS: observe atentamente a forma como é enviado os parâmetros de cada função!
OBS: Percebe que dessa vez não podemos declarar o vetor que nem no Exemplo 1? 
Justamente por não saber a quantidade máxima de elementos do vetor! Vamos supor que o enunciado falasse que são 50 alunos, seria assim a declaração: Aluno *alunos[50];
Mas como não foi especificado a quantidade, a >>ÚNICA<< solução é alocando dinamicamente o vetor, assim como os ponteiros que vão ser armazenados! (Mesmo esquema do Exemplo
1)
Lembrando que só podemos alocar um vetor se já tivermos uma quantidade exata. Nesse caso, foi o usuário que digitou a quantidade de alunos logo no começo do programa.
*********************************************************************************
Dica Importante: Não precisa decorar quando usar ** ou *!!!Pra facilitar, vamos sempre pensar assim: 
alunos = (Aluno **) malloc (n * sizeof(Aluno *));
alunos = (O que queremos) malloc (quantidade * sizeof(O que queremos colocar em cada posição do vetor))
Ou seja, queremos converter o retorno da função malloc em Aluno ** que é um vetor de ponteiro para Aluno! 'n' é a quantidade de posições do vetor (Quantidade de alunos), então é só multiplicar pelo tamanho do que queremos armazenar em cada posição do vetor, no caso, um ponteiro para Aluno em cada posição!
*********************************************************************************
OBS: É muito importante entender como se cria do zero um vetor de ponteiro que nem fizemos na função main do Exemplo 1 e 2! Mas o que costuma ser mais cobrado em prova são as outras funções! Qualquer função que manipula vetor de ponteiro! Por isso é de grande importância já ir se acostumando com a nova notação nos parâmetros. (char **nomes, Aluno ** a , etc)
Mas mesmo assim, em raros casos podem pedir pra criar a main, então é sempre bom já estar preparado, né não? :)
E depois de ler esse resumo todo, se tiver na prova algo com dois asteriscos, não vai se assustar mais né! =D
------------------------------------//-------------------------------------------

Teste o Premium para desbloquear

Aproveite todos os benefícios por 3 dias sem pagar! 😉
Já tem cadastro?

Outros materiais