Vetores podem ser entendidos como um grupo de posições de memória contíguas relacionadas entre si pelo fato de possuírem o mesmo nome e tipo. Por exemplo, uma declaração da forma
int V[10] ;corresponde a reservar uma área contígua de memória suficiente para armazenar 10 inteiros. Para referenciar uma particular posição ou elemento do vetor, especificamos o nome do vetor e o número da posição do vetor (índice) entre colchetes. Isto é, os 10 elementos do vetor V podem ser referenciados respectivamente por
V[0] V[1] V[2] V[3] V[4] V[5] V[6] V[7] V[8] V[9]
int V[10] ; /* declaração */ int x ; V[0] = 10; /* atribuições */ V[1] = 20 ; V[2] = 30 ; x = V[0]+V[1]+V[2] ; /* operações envolvendo elementos do vetor */
Note também a diferença entre a declaração do vetor e a referência a uma posição do vetor. Por exemplo, no trecho a seguir
int V[10] ; /* declaração */ V[6] = 100 ; /* atribuição de valor 100 na posição 6 do vetor */10 significa o tamanho (quantidade de elementos) do vetor, enquanto 6 significa uma posição específica, a sexta posição, no vetor (supondo que as posições começam em 0). Note que não faz sentido fazer referências do tipo V[-1] nem V[10], já que os índices do vetor variam de 0 a 9.
char c[10] ; /* vetor de caracteres */ double num[100] ; /* vetor de reais */
Voce poderia ter 11 contadores, nota0, nota1, nota2 e assim por diante até nota10, ou então você pode ter um vetor de 11 posições, cada uma para contabilizar a quantidade de notas correspondentes.
Em outras palavras, em vez de escrever
int nota0, nota1, nota2, nota3, nota4, nota5, nota6, nota7, nota8, nota9, nota10 ; int i, nalunos ; double p ; nota0 = nota1 = nota2 = nota3 = nota4 = nota5 = 0 ; nota6 = nota7 = nota8 = nota9 = nota10 = 0 ; scanf{"%d", &nalunos} ; for(i=0; i<nalunos; i++) { scanf("%lf", &p) ; if(0<=p && p<1) nota0++ ; if(1<=p && p<2) nota1++ ; ... if(9<=p && p<10) nota9++ ; if(p==10) nota10++ ; }pode-se escrever ``equivalentemente'' o seguinte :
int nota[11] ; int i, nalunos ; double p ; for(i=0; i<11; i++) { nota[i] = 0 ; } scanf{"%d", &nalunos} ; for(i=0; i<nalunos; i++) { scanf("%lf", &p) ; nota[(int)p]++ ; }
Exemplo: Dado um inteiro positivo n e uma seqüência com n inteiros, imprimir a seqüência na ordem inversa a da leitura.
#include <stdio.h> #define MAX 100 int main() { int V[MAX] ; int i, n ; printf("Qual o tamanho da sequencia ?\n") ; scanf("%d", &n) ; if(n>MAX) { printf("A sequencia e' muito longa.\n") ; printf("Ela deve conter no máximo %d elementos\n", MAX) ; return 0 ; } for(i=0; i<n ; i++) scanf("%d", &V[i]) ; for(i=n-1; i>=0 ; i--) printf("%d", V[i]) ; return 0 ; }Observe que no comando scanf(%d", &V[i]) ; voce deve usar o operador & (mais precisamente, &V[i]) pois o scanf vai atribuir o valor lido à posição i do vetor, isto é, à variável V[i], e para isso deve-se passar o endereço da variável V[i].
Já quando queremos imprimir o valor que está na ppsição i do vetor, basta considerarmos a variável V[i]. Assim, o comando printf(%d", V[i]) ; imprime o valor de V[i].
E se a seqüência fosse uma frase (isto é, uma seqüência de caracteres) terminada por ponto ? Como seria o programa ? (Desconsidere o ponto na saída).
#include <stdio.h> #define MAX 100 int main() { char c, seq[MAX] ; int i, cont ; cont = 0 ; printf("Digite uma frase terminada por ponto.\n") ; scanf("%c", &c) ; /* le o primeiro caractere */ while(c != '.' && cont<MAX) { seq[cont] = c ; cont++ ; scanf("%c", &c) ; /* le o proximo caractere */ } if(c != '.') { /* encheu o vetor antes de terminar a leitura */ printf("Seqüência muito longa.") ; printf("Deve conter no máximo %d caracteres.\n", MAX) ; return 0 ; } /* imprime a seq. lida, de tras para frente */ for(i=cont-1; i>=0 ; i--) printf("%c", seq[i]) ; return 0 ; }
O conceito de vetores pode ser extendido para vetores multi-dimensionais. Em vez de pensarmos em algo linear, com um índice, podemos pensar em estruturas com mais de um índice. Um exemplo simples é a caso das matrizes, que podem ser pensadas como vetores de duas dimensões.
Vejamos como declaramos uma matriz (vetor de duas dimensões) :
int M[20][10] ; /* matriz de inteiros com 20 linhas e 10 colunas */ char C[5][100] ; /* matriz de caracteres com 5 linhas e 100 colunas */
Da mesma forma que com os vetores unidimensionais, os índices sempre variam de 0 a tamanho-1.
Posso ter vetores com várias dimensões:
int M[20][10][5] ; int exemplo[10][20][30][40] ;
A diferença é que precisamos fazer referência a um maior número de índices. Numa matriz M, um elemento na linha i e coluna j deve ser referenciado por M[i][j].
Suponha que é dado uma matriz A de inteiros com m linhas e n colunas. Para imprimir esta matriz, o seguinte trecho de código pode ser utilizado:
for(i=0; i<m; i++) { for(j=0; j<n; j++) printf("%d ", A[i][j]) ; printf("\n") ; /* muda linha apos imprimir linha i */ }
/* imprime os n primeiros elementos do vetor V */ void imp_vetor(int V[MAX], int n) { int i ; for(i=0; i<n; i++) { printf("%d ", V[i]) ; } }
/* imprime os elementos das mxn primeiras posicoes da matriz A */ void imp_vetor(int A[MAX][MAX], int m, int n) { int i, j ; for(i=0; i<m; i++) { for(j=0; j<n; j++) printf("%d ", A[i][j]) ; printf("\n") ; /* muda linha apos imprimir linha i */ } }
/* le n inteiros e armazena no vetor V. Assume que n <= MAX */ void le_vetor(int V[MAX], int n) { int i ; for(i=0; i<n; i++) { scanf("%d ", &V[i]) ; } }
/* le uma matriz m x n de inteiros e armazena na matriz A. Assume que m <= MAX e n <= MAX */ void le_matriz(int A[MAX][MAX], int m, int n) { int i, j ; for(i=0; i<m; i++) for(j=0; j<n; j++) scanf("%d ", &A[i][j]) ; }
/* Exemplo ilustrando como usar as funções acima */ #include <stdio.h> #define MAX 100 void le_vetor(int V[MAX], int n) ; void imp_vetor(int V[MAX], int n) ; void le_matriz(int A[MAX][MAX], int m, int n) ; void imp_matriz(int A[MAX][MAX], int m, int n) ; int main() { int matriz[MAX][MAX], vetor[MAX] ; int m, n, p ; printf("Qual o tamanho da sequencia ?\n") ; scanf("%d", &p) ; if(p > MAX) { printf("Sequencia muito longa") ; return 0 ; } printf("Digite a sequencia de inteiros\n") ; le_vetor( vetor, p ) ; imp_vetor( vetor, p ) ; printf("Qual o tamanho da matriz?\n") ; scanf("%d %d", &m, &n) ; if(m > MAX || n > MAX) { printf("Matriz muito grande.") ; return 0 ; } printf("Digite a matriz\n") ; le_matriz( matriz, m, n ) ; imp_matriz( matriz, m, n ) ; return 0 ; }
Note que, apesar das funções le_vetor e le_matriz alterarem o conteúdo das variáveis vetor e matriz, respectivamente, na chamada da função não foi utilizado o operador de endereço &.
Quando um vetor ou matriz é passado como argumento de uma função, C automaticamente passa o endereço deles. Isso acontece pois vetores ou matrizes são, na verdade, apontadores. Assim, na declaração
int V[10] ; /* declaração */o valor de V é o endereço do primeiro elemento do vetor, isto é, V e &V[0] são a mesma coisa. Para todos os efeitos, V é um apontador e V[i] pode ser encarado como uma variável comum.
Entendendo um pouco melhor, se eu tiver
int V[10] ; /* declaração */ int *p ;as seguintes atribuições são equivalentes :
p = V ; p = &V[0] ;
Neste ponto, vale a pena reforçarmos o conceito de ponteiros. Lembrem-se que usando ponteiros é possível fazer referência ao conteúdo de alguma posição de memória. Por exemplo, se p é um ponteiro para int, isto é, se
int *p ;então *p é o conteúdo na posição de memória cujo endereço é p. Ou seja, no trecho a seguir,
int *p ; int x ; p = 1054 ; x = *p ;a variável x recebe o valor que se encontra na posição (endereço) 1054 da memória (claro que deve-se tomar cuidado para que a posição de memória referenciada faça sentido!)
Ponteiros são também, antes de mais nada, variáveis. Logo, podemos ``operar'' com elas. Isto é, podemos fazer p = p + 10;, por exemplo. No entanto, neste caso a operação + não é a operação usual de adição. Se o valor de p é 3000, na aritmética convencional o resultado da operação seria 3010. No entanto, quando um inteiro é adicionado ou subtraído de um ponteiro, o valor do ponteiro não é aumentado ou diminuído por aquele inteiro, mas sim por aquele inteiro vezes o tamanho do objeto apontado pelo apontador.
Por exemplo, supondo que un inteiro ocupa 4 bytes, a operação p = p + 10; quando p vale 3000, resultaria em algo como 3000 + 4*10.
Portanto, a operação p++ não incrementa o valor de p de apenas 1, mas sim de 4. Logo, tome cuidado ao escrever coisas como
*p++ ;achando que está incrementando o valor do elemento apontado por p. O comando acima primeiro incrementa o valor de p (isto é, muda o elemento apontado) e depois referencia o valor apontado. O correto, caso voce queira incrementar o valor do elemento apontado por p, é fazer:
(*p)++ ;
Concluindo, da mesma forma que
p = V ; e p = &V[0] ;são equivalentes,
p = V+3 ; e p = &V[3] ;são equivalentes, e portanto,
*(V+3) e V[3]também são equivalentes.
Concluindo: na passagem de variáveis ``comuns'' como argumento para uma função, vimos que podemos passar:
Neste caso, as alterações feitas dentro da função poderão alterar o valor da cópia e não da variável original.
Neste caso, se o parâmetro for um apontador (do tipo int *p, por exemplo), então você pode alterar o valor da variável original, alterando o conteúdo da posição de memória cujo endereço é aquele que você passou para a função (isto é, se você faz *p = 0, você está atribuindo 0 à variável original. Note que *p = 0 e p=0 são diferentes, e se p é um apontador, então muito provavelmente a segunda atribuição vai resultar em algum tipo de erro).
Quando vetores ou matrizes são passadas para uma função, o que é passado são os respectivos endereços. Vetores e matrizes são tratados como ponteiros. Logo, para vetores e matrizes só é possível o segundo caso acima. Isto significa que qualquer alteração no valor do vetor ou da matriz dentro da função alterará o vetor ou a matriz original.
As duas funções abaixo recebem um vetor com n (0< n <= MAX) inteiros e ordenam os números do vetor, isto é, após a execução da função, a seqüência que estava armazenada no vetor fica ordenada.
Ordenação por seleção: o algoritmo realiza n-1 passadas. Após a passada i, garante que o elemento que deveria ocupar a posição i na seqüência ordenada está de fato na posição i do vetor.
Explicando melhor, na primeira passada (i=0), o elemento na posição 0 é comparado sucessivamente com os elementos nas posições 1, 2, até n-1. Sempre que o elemento que se encontra na posição 0 é maior que o elemento V[j] com o qual ele está sendo comparado, ambos trocam de posição. Isto garante que na posição 0 mantenho o menor elemento dentre todos os elementos nas posições de 0 a j. Uma vez que isso é feito até o último elemento da seqüência, quando a primeira passada termina, temos certeza que o menor elemento da seqüência se encontra na posição 0 do vetor. Depois, basta repetir o mesmo processo para o restante do vetor (isto é feito nas passadas 2, 3, 4, até n-2).
void ordena_por_selecao( int V[MAX], int n) { int i, j, aux ; for(i=0; i<n-1; i++) for(j=i+1; j<n; j++) if(V[i] > V[j]) { aux = V[i] ; V[i] = V[j] ; V[j] = aux ; } }
Ordenação por bolhas (?) ``Bubble sorting''; Aqui a idéia é que a cada passada os elementos leves (pequenos) vão subir uma posição (vão deslocar em direção ao início do vetor) enquanto os mais pesados vão deslocar em direção ao final do vetor. O algoritmo consiste em compararmos, a cada passada, cada dois elementos subsequentes no vetor e trocá-los de posição sempre que eles estiverem invertidos.
void ordena_por_bolha( int V[MAX], int n) { int passada, j, aux ; for(passada=1; passada<n; passada++) for(j=0; j<n-1; j++) if(V[j] > V[j+1]) { aux = V[j+1] ; V[j+1] = V[j] ; V[j] = aux ; } }No algoritmo acima, um elemento grande que se encontra no início do vetor pode ser ``deslocado'' até o final do vetor em uma passada, mas um elemento pequeno que se encontra no final do vetor poderá deslocar no máximo uma posição para a frente (em direção ao início do vetor) em uma passada. Logo, no pior caso serão necessárias n-1 passadas até que o menor elemento que se encontrava na última posição desloque até a posição inicial do vetor.
Em alguns casos, um número menor de passadas pode ser suficiente para termos uma seqüência ordenada no vetor. Tente escrever uma função que evita passadas desnecessárias.
Para aqueles com maior interesse, mais detalhes podem ser encontrados nesta página.
Escreva uma função que recebe um vetor com n (n>0) números reais e também um número real x, e devolve a posição de ocorrência de x no vetor caso ele ocorra e -1 caso ele não ocorra no vetor. Lembre que as posições do vetor variam de 0 a n-1. Suponha que a constante MAX que define o tamanho do vetor está adequadamente definida e que n <= MAX.
/* funcao que verifica se x ocorre em V. Caso ocorra, devolve a posicao de ocorrencia, e caso nao ocorra, devolve -1 */ int procura( double V[MAX], int n, double x ) { int i ; for(i=0; i<n; i++) if(V[i] == x) return i ; return -1 ; }
/* função que calcula o produto da matriz A (m x n) pela matriz B (n x p) e devolve o produto na matriz C */ void produto( int A[MAX][MAX], int B[MAX][MAX], int C[MAX][MAX], int m, int n, int p) { int i, j, k ; /* funcao resultante C tera' dimensao m x p */ /* Assim, o for para o i e o j servem para */ /* percorrer cada posicao da matriz resultante */ for(i=0; i<m; i++) for(j=0; j<p; j++) { /* Calculo do resultado para a posicao (i,j) : */ /* corresponde a soma do produto dos elemento na linha i */ /* da matriz A pelos respectivos elementos na coluna j */ /* da matriz B. */ C[i][j] = 0 ; for(k=0; k<n; k++) C[i][j] = C[i][j] + A[i][k]*B[k][j] ; } }