Introdução aos vetores em C
Como explicado no texto introdutório ao conceito de vetores, um vetor é uma sequência de dados ocupando posições consecutivas de memória e por isso existe uma ordem natural entre seus elementos, o primeiro elemento, o segundo e assim por diante. A grande vantagem de usar vetor é poder trabalhar com muitas variáveis utilizando um único nome para esse "agregado" de variáveis. Para isso existe uma sintaxe especial para pegar cada elemento do vetor, em posições específicas, como o primeiro elemento ou o décimo elemento.
A linguagem C trata o conceito de vetor de um modo bastante próximo ao modo como é possível implementar esse conteito. Ilustraremos isso mostrando que podemos usar apontadores para pegar elementos dos vetores.
1. Definindo e imprimindo um vetor
Pode-se construir um vetor em C fornecendo todos seus elementos, como em:
Pode-se também construir de modo "dinâmico", dependendo dos valores que variáveis assumem em meio ao código.
Para isso deve-se declarar o máximo de posições que o vetor ocupará (como
No exemplo a seguir construímos um vetor com MAX elementos, iniciando no 10+i e seguindo até o 10+n-1. Nesse exemplo indicamos, em um comentário, como usar apontador (*(v+i)=10+i) para definir os elementos de um vetor (usando aritmética de ponteiros).
#include <stdio.h> // Declarar uma constante (para ser usada como dimensao maxima do vetor)#define MAX 10 // Funcao para imprimir elementos de vetor em uma mesma linha // Nao precisa saber do total alocado para o vetor efetivo, pois para pegar o elemento i basta // saber a primeira posicao, digamos 'apont' e pegar '*(apont + i)'void imprime_vetor1 (int vet[],int tam) {int i; for (i=0; i<tam; i++)printf ("%d ", vet[i]);printf ("\n");// ao final quebre a linha } // Esta funcao define os valores para o vetor (passagem por referencia/endereco) // Lembrando: todo vetor e' passado por referencia 'a funcao (logo // alterar seus valores localmente implica em alterar os valores do // parametro efetivo! // Por ser vetor como parametro, nao precisa declarar a dimensao (nao precisa reservar espaco, e' apenas uma referencia) // Mas se fosse matriz precisaria saber dimensao colunavoid definirVetor (int v[],int tam) {int i; for (i=0; i<tam; i++)// definir vetor/lista com: (10,11,12,13,...10+n-1) v[i] = 10+i;// Aqui altera' o 'parametro efetivo'! Tambem pode-se usar como apontador: *(v+i) = 10+i; }int main (void ) {int n=10, vet[MAX];// alocar MAX posicoes para o vetor 'vet' (pode-se usar menos) definirVetor(vet, n);// parametro efetivo 'vet' por ser vetor, se alterado na funcao, altera o efetivo! // Imprime o vetor/lista de uma so' vez (na mesma linha) printf ("\nO vetor: "); imprime_vetor1(vet, n);// em C NAO e' possivel imprimir todos os dados de um vetor => fazer uma funcao... return 0; }
Importante.
Em C, usar um vetor como parâmetro (formal) em função implica que esta usando passagem por referência (ou endereço)!
Portanto, dentro da função, se alterar o valor de qualquer elemento do vetor (parâmetro formal), isso implicará que, durante a execução,
o seu parâmetro efetivo (aquele que é passado efetivamente para função) terá o elemento correspondente alterado.
Por este motivo, no exemplo acima, ao fazer uma atribuição dentro da função (v[i] = 10+i;), estamos na verdade
alterando os valores do parâmetro efetivo vet,
por esse motivo usamos o nome passagem por referência (ou endereço),
é como se o parâmetro formal v[] fosse uma referência (um "apelido") para o parâmetro efetivo vet[].
Se tiver dúvidas,
reveja o texto sobre
parâmetros de funções.
2. Defininido uma cadeia de caracteres ("string")
Em C é possível definir um vetor com caracteres, formando uma palavra, ou seja, uma cadeia de caracteres, que abreviadamente é denominada "string". Para isso pode-se fazer uma atribuição como constante ou ler os dados como palavra. Isso é ilustrado no exemplo abaixo.
#include <stdio.h>#include <string.h>// para usar a funcao 'strlen(...)' void main (void ) {int i;char string[] = "0123456789abcdefghih";// (1) - define a "string" // Imprimir cada caractere da string com seu codigo ASCII printf ("Imprime caracteres e seus codigos ASCII na forma de tabela\nColuna 1 => caractere; coluna 2 => seu codigo ASCII\n"); for (i=0; i<strlen(string); i++)printf ("%20c : %3d\n", string[i], string[i]);// em C inteiro pode ser "lido" como caractere e vice-versao (via codigo ASCII) // Pode-se imprimir como uma frase toda usando o formatador %s printf ("Frase: %s\n", string);// A string tem 20 caracteres, testando os caracteres 19 'h' e 20 '\n' printf ("A string tem 20 caracteres, testando os caracteres 19 '%c' e 20 '\\n' em ASCII=%d\n", string[19], string[20]); }
Deve-se notar que em C, ao fazer a leitura de uma "string" (por exemplo, via teclado com
3. Vetores e apontadores
Uma vantagem didática da utilização da linguagem C em um curso introdutório de programação é que essa é uma linguagem que não esconde muitos detalhes de como os conceitos são implementados. Isso possibilita mostrarmos os fundamentos da computação, os princípios elementares que permitem a construção de sistemas mais sofisticados. Nesse sentido o conceito de vetor é ilustrativo, pois sua implementação depende da existência de apontadores.
Ao declarar um vetor, significa que associamos um nome de variável (o "nome do vetor") a uma sequência de posições de memória, todas de mesmo tipo (logo de mesmo tamanho). Na verdade essa variável guarda o endereço inicial do vetor, o endereço de seu primeiro elemento.
O nome de um vetor em C é na verdade uma variável apontadora e esse fato retoma a observação de C não esconder detalhes. Além disso a linguagem permite calculemos a posição de um elemento de vetor somando ao seu endereço inicial o número de elementos que desejamos pular (e.g. v+3). Fazendo isso, automaticamente o compilador traduz essa expressão deslocando o número de bytes do tipo envolvido e por isso, fazer v+3, é o mesmo que pegar o elemento da posição 3 ou v[3].
Como a declaração de uma variável apontadora é feita usando um asterisco (como em
#include <stdio.h>void imprimeVetor (int *apont,int n) {int i;printf ("Vetor:");// supondo X ser o primeiro endereco em 'apont' for (i=0; i<n; i++)printf (" %d", *(apont+i));// pegar elemento na posicao 'X + i*sizeof(int) - isso e' o mesmo que apont[i] printf ("\n"); }int main (void ) {int vet1[] = { 5, 4, 3, 2, 1 };// define vetor com 5 posicoes de 0 ate' 4 int *apont;int N = 5; imprimeVetor(vet1, N);// passa para "apont" o endereco inicial do vetor return 0; }
Assim, também é possível alterar os valores em qualquer posição do vetor a partir do endereço.
Por exemplo, se fizermos
apont = vet1;
for (i=0; i<5; i++) *(apont+i) = 5-i;
geraríamos novamente o vetor vet1 = { 5, 4, 3, 2, 1}.
Experimente!
4. Funções úteis da biblioteca string.h
A implementação da função strlen (que está na biblioteca string.h) usa esse fato, a existência de um
caractere especial que só pode ser utilizado em "string" como finalizador para encontrar o tamanho da "string".
Assim, se fizer um comando como string[10] = '\0';
Existem outras funções na biblioteca string.h, uma delas é a strcpy(str1,str2) que copia o conteúdo da "string" str2 sobre a str1, desde que a declaração delas seja compatível (ou seja, str2 não pode ter mais caracteres do que a declaração inicial de str1). Outra função bastante útil é a strcmp que compara lexicograficamente (ordem dos dicionários) duas "strings", do seguinte modo, o comando strcmp(str1, str2) devolve
5. Defininido vetor de vetor (ou matriz)
Uma vez que um vetor/lista é armazenado consecutivamente na memória, então podemos armazenar vários vetores também consecutivamente para obter algo que represente (e seja tratado como) uma matriz. Como na imagem abaixo, em que cada alocamos nl vetores consecutivos na memória, cada vetor com nc posições de memória, desde vet[0] até vet[nc-1]. Assim, podemos ver este agregado de dados como uma matriz mat[][], cuja primeira linha é o primeiro vetor e assim por diante. Isso está ilustrado na imagem abaixo.
Desse modo obtemos uma estrutura com dois índices que pode ser manipulada como uma matriz (como em mat[i][j]). No exemplo abaixo ilustramos isso de dois modos, imprimindo as linhas da matriz e mostrando que pode-se passar como parâmetro uma linha de matriz para uma função que tenha como parâmetro formal um vetor.
#include <stdio.h>#define MAXL 10// declarar constantes (para ser usada como dimensoes maximas da matriz) #define MAXC 10// Funcao que soma elementos no vetor (precisa do numero de elementos no vetor para saber quando parar) int soma_linha (int linha[],int n) {// usando como parametro um vetor (NAO precisa da dimensa, explicacao abaixo) int i, soma = 0; for (i=0; i<n; i++) soma += linha[i]; return soma; }// Copiar aqui a funcao do exemplo 1: imprime_vetor1 // Funcao para imprimir linhas de matriz (linha por linha): usa 'imprime_vetor1(...)' para imprimir uma linha // Precisa do numero de colunas pois na pratica os elementos estao em linhas de MAXC elementos void imprime_matriz (int mat[][MAXC],int numLinhas,int numColunas) {int i; for (i=0; i<numLinhas; i++) imprime_vetor1(mat[i], numColunas);// para pegar inicio de 'mat[i]' usa-se o valor de MAX }void main (void ) {int mat1[MAXL][MAXC];// alocar MAXL*MAXC posicoes para a matriz 'mat1' ('mat1' e' um vetor de vetor) int n = 10, i, j, nl, nc, prim, seg;// Vetor de vetor ou matriz nl = 3; nc = 4; for (i=0; i<nl; i++)// foram alocados na memoria MAXL*MAXC for (j=0; j<nc; j++)// mas usaremos apenas nl*nc mat1[i][j] = i*nc + j;// Pegando o primeiro e segundo elemento da terceira linha de mat1: mat1[2][1] prim = mat1[2][0]; seg = mat1[2][1];// pegar primeiro e segundo elemento da linha mat1[2] printf ("O primeiro e segundo elemento da terceira linha da matriz: %d e %d\n", prim, seg);// Imprime a matriz printf ("\nA matriz: \n");// informe o que vira impresso a seguir (na mesma linha devido ao parametro end="" imprime_matriz(mat1, nl, nc);// Verifique que cada linha da matriz e' de fato um vetor // Usar a funcao 'somalinha(...)' que soma elementos em vetor for (i=0; i<nl; i++)printf ("Soma da linha %2d = %3d\n", i, soma_linha(mat1[i], nc));// note que cada linha 'mat1[i]' tem 'nc' elementos uteis }
6. Defininido vetor de vetor (ou matriz) de modo dinâmico.
Uma vez que um vetor é armazenado consecutivamente na memória, então podemos armazenar vetores de modo consecutivo para obter algo que represente (e seja tratado como) matriz. Neste caso obtemos uma estrutura com dois índices (como em mat[i][j]). No exemplo abaixo ilustramos isso de dois modos, imprimindo as linhas da matriz e mostrando que pode-se passar como parâmetro uma linha de matriz para uma função que tenha como parâmetro formal um vetor.
#include <stdio.h>#include <stdlib.h>// para 'malloc' e 'free' #define MAXL 10// declarar constantes (para ser usada como dimensoes maximas da matriz) #define MAXC 10 // Copiar aqui a funcao do exemplo 1: imprime_vetor1 // Funcao para imprimir elementos de vetor em uma mesma linha - usando aritmetica de ponteirovoid imprime_vetor2 (int *pont,int tam) {int i; for (i=0; i<tam; i++)// "*(pont+i)" significa, pegar a posicao do primeiro elemento printf ("%d ", *(pont+i));// (em 'linha') e deslocar i posicoes (do tipo 'int') printf ("\n");// ao final quebre a linha } // Funcao que soma elementos no vetor: outra versao tratando o vetor como apontadorint soma_linha (int *linha,int n) {// usando como parametro apontador para inteiro int i, soma = 0;// Uma vez que um vetor e' na verdade uma sequencia de posicoes de memoria, C // simplesmente anota o endereco da primeira posicao no nome do vetor 'linha', // por isso e' a mesma coisa que declarar como parametro formal o apontador ' for (i=0; i<n; i++) soma += *(linha + i); return soma; } // Funcao para imprimir linhas de matriz (linha por linha): usa funcao 'imprime_vetor1(...)' // Precisa do numero de colunas pois na pratica os elementos estao em linhas de MAXC elementosint *linha'void imprime_matriz (int mat[][MAXC],int numLinhas,int numColunas) {int i; for (i=0; i<numLinhas; i++) imprime_vetor2(mat[i], numColunas);// para pegar inicio de 'mat[i]' usa-se o valor de MAX }void main (void ) {int *vet2;// declara um apontador para inteiro que depois apontara' para o vetor dinamico int mat1[MAXL][MAXC];// alocar MAX*MAX posicoes para a matriz 'mat1' ('mat1' e' um vetor de vetor) int (*apontador)[MAXC];// declara um apontador para linhas com MAXC elementos, se usar apenas ' int *apontador',// '*(apontador[i]+j) = i*nc+j;' poderia resultar advertencia indicada em (1) int n = 10, i, j, nl, nc, prim, seg;// Vetor de vetor ou matriz // Definir a amtriz de 2 modos (com indices de matriz ou como apontador) nl = 3; nc = 4; apontador = mat1;// pegue o endereco inicial da matriz - (1) se usar "int *apontador", na declaracao // acima, poderia resultar advertencia: "warning: assignment from incompatible pointer type" for (i=0; i<nl; i++) for (j=0; j<nc; j++)// ilustra 2 forma de manipular matriz/vetor if (j%2)// se j impar => definir com indices mat1[i][j] = i*nc + j; else// se j par => definir com apontador *(apontador[i] + j) = i*nc + j;// deslocar i linhas (i*MAXC) e depois deslocar j elementos // Pegando o primeiro e segundo elemento da terceira linha de mat1: mat1[2][1] prim = mat1[2][0]; seg = mat1[2][1];// pegar primeiro e segundo elemento da linha mat1[2] printf ("O primeiro e segundo elemento da terceira linha da matriz: %d e %d\n", prim, seg);// Imprime a matriz printf ("\nA matriz: \n");// informe o que vira impresso a seguir (na mesma linha devido ao parametro end="" imprime_matriz(mat1, nl, nc);// Verifique que cada linha da matriz e' de fato um vetor // Usar a funcao 'somalinha(...)' que soma elementos em vetor for (i=0; i<nl; i++)printf ("Soma da linha %2d = %3d\n", i, soma_linha(mat1[i], nc));// note que cada linha 'mat1[i]' tem 'nc' elementos uteis // Agora definindo vetor com tamanho dinamico n = 6; vet2 = malloc(sizeof(int) * n);// aloca dinamicamente n posicoes para inteiros ('Memory ALLOCation') // Define vetor com n elementos exatamente for (i=0; i<n; i++) *(vet2+i) = 10 + i;// Imprime o vetor/lista de uma so' vez (na mesma linha) printf ("\nO vetor definido via alocacao dinamica: "); imprime_vetor1(vet2, n);// em C NAO e' possivel imprimir todos os dados de um vetor => fazer uma funcao... free(vet2);// depois de usar pode-se devolver as posicoes para sistema operacional }
Leônidas de Oliveira Brandão
http://line.ime.usp.br
Alterações:
2020/10/20: pequenos acertos léxicos e na introdução da seção 3 (ajuda da Priscila Lima), acerto em comentário no código "Exemplo 2" (ajuda do Felipe C. Lourenço)
2020/08/13: novo formato, pequenas revisões
domingo, 17 May 2020, 18:30
segunda, 03 May 2018, 16:00