Curso de C
Listas e Matrizes
Você está em: MarMSX >> Cursos >> C
As variáveis cumprem bem com o seu papel quando se trata de dados simples. Entretanto, se desejarmos armazenar vários dados, como, por exemplo, o nome de 10 pessoas, o uso das variáveis se torna complicado. Exemplo:
#include <stdio.h>
char *nome1, *nome2, *nome3, *nome4, *nome5, *nome6, *nome7, *nome8, *nome9, *nome10;
void main(void)
{
printf("Entre o nome da 1a. pessoa: ");
scanf("%s",nome1);
printf("\nEntre o nome da 2a. pessoa: ");
scanf("%s",nome2);
printf("\nEntre o nome da 3a. pessoa: ");
scanf("%s",nome3);
printf("\nEntre o nome da 4a. pessoa: ");
scanf("%s",nome4);
printf("\nEntre o nome da 5a. pessoa: ");
scanf("%s",nome5);
printf("\nEntre o nome da 6a. pessoa: ");
scanf("%s",nome6);
printf("\nEntre o nome da 7a. pessoa: ");
scanf("%s",nome7);
printf("\nEntre o nome da 8a. pessoa: ");
scanf("%s",nome8);
printf("\nEntre o nome da 9a. pessoa: ");
scanf("%s",nome9);
printf("\nEntre o nome da 10a. pessoa: ");
scanf("%s",nome10);
}
Imagine se desejássemos verificar nessa lista se há alguma pessoa chamada "Bruno". Seria mais uma dezena de linhas de código. E para 1000 pessoas ? :O
É aí que entram os vetores (listas). Eles servem para armazenar vários dados do mesmo tipo em um lugar com o mesmo nome, onde cada dado é referenciado por um número, que indica sua posição na lista.
Variáveis Vetor
┌─────────┐ ┌─────────┐
│ nome1 │ │ nome │
├─────────┤ ├─────────┤
│ "Carla" │ │ "Carla" │ 0
└─────────┘ ├─────────┤
│ "Pedro" │ 1
┌─────────┐ ├─────────┤
│ nome2 │ │ ... │
├─────────┤ ├─────────┤
│ "Pedro" │ │ "Yago" │ 9
└─────────┘ └─────────┘
...
┌─────────┐
│ nome10 │
├─────────┤
│ "Yago" │
└─────────┘
Vetor
O vetor (em inglês "array") é uma lista, no qual é feita uma reserva de um espaço de memória para armazenar N variáveis do mesmo tipo e em posições contíguas.
A sintaxe de declaração de vetores em C é a seguinte:
<tipo_de_dado> <identificador>[<tamanho_do_vetor>];
Onde:
- tipo_de_dado - indica o tipo de dado armazenado pela variável.
- identificador - é o nome da variável.
- [] - indica que a variável é um vetor. Pode vir antes ou depois da variável.
- tamanho_do_vetor - tamanho do vetor (N), sempre variando de 0 a N-1.
Ex:
int nota[10];
Cria um vetor de 10 posições de números inteiros, variando de 0 a 9.
O vetor é acessado informando-se o número da posição em que desejamos ler ou inserir dados. Para isso, deve-se colocar o nome do vetor, mais o índice dentro dos colchetes. Exemplo:
printf("%d", nota[2]);
Este mecanismo é semelhante ao DIM do Basic:
Basic C
10 DIM NOTA(10) int nota[10];
20 NOTA(2) = 8 nota[2] = 8;
30 PRINT NOTA(2) printf("%d", nota[2]);
Importante: Um vetor em C varia de 0 a N-1. Assim, "nota[2]" retorna o terceiro elemento da lista.
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│xxx│xxx│ 8 │xxx│xxx│xxx│xxx│xxx│xxx│xxx│
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
0 1 2 3 4 5 6 7 8 9
↑
Podemos reescrever o programa dos 10 nomes de uma forma mais simples:
#include <stdio.h>
char *nome[10];
void main(void)
{
int i;
for (i=0; i<10; i++)
{
printf("\nEntre o nome da %da. pessoa: ",i);
scanf("%s",nome[i]);
}
}
Atribuição inicial
É possível declarar um vetor e ao mesmo tempo atribuir seus valores. Exemplo:
#include <stdio.h>
int notas[5] = { 8, 6, 7, 9, 10 };
main()
{
int i;
for (i=0; i<5; i++)
printf("Nota %d: %d\n", i+1, notas[i]);
}
Saída:
Nota 1: 8
Nota 2: 6
Nota 3: 7
Nota 4: 9
Nota 5: 10
Obs: o tamanho do vetor declarado deverá ser exatamente igual à quantidade de elementos inseridos entre as chaves.
int notas[5] = { 8, 6, 7, 9, 10 };
| ^
| |
+-------------+
Matriz
Podemos criar uma matriz, isto é, um vetor com D dimensões, objetivando novas formas de organizar os dados.
Para indicar uma matriz de D dimensões, colocamos D pares de colchetes ao lado do nome da variável. Por exemplo, uma matriz de três dimensões:
int matriz_3d[10][20][2];
Que representa um cubo, cuja largura é 10, altura é 20 e profundidade é igual a 2.
No exemplo a seguir, vamos criar uma matriz de duas dimensões (tabela) para guardar o dia, o mês e o ano do nascimento de 20 pessoas.
Em uma tabela, cada pessoa ocupa uma linha, enquanto que cada atributo (dia, mês e ano) ocupa uma coluna. Assim, é necessário criar uma tabela de 20 linhas por 3 colunas.
include <stdio.h>
int data[20][3];
int i;
void main(void)
{
for (i =0; i<20; i++)
{
printf("Pessoa numero %d: ", i);
printf("Dia: ");
scanf("%d",&data[i][0]);
printf("Mes: ");
scanf("%d",&data[i][1]);
printf("Ano: ");
scanf("%d",&data[i][2]);
}
}
Ao criarmos a tabela acima, usamos o primeiro colchete para determinar o número total de linhas e o segundo para o número total de colunas.
A aparência da tabela criada é a seguinte:
|
DIA |
MES |
ANO |
PESSOA 0 |
|
|
|
PESSOA 1 |
|
|
|
PESSOA 2 |
|
|
|
PESSOA 3 |
|
|
|
PESSOA 4 |
|
|
|
PESSOA 5 |
|
|
|
PESSOA 6 |
|
|
|
PESSOA 7 |
|
|
|
PESSOA 8 |
|
|
|
PESSOA 9 |
|
|
|
PESSOA 10 |
|
|
|
PESSOA 11 |
|
|
|
PESSOA 12 |
|
|
|
PESSOA 13 |
|
|
|
PESSOA 14 |
|
|
|
PESSOA 15 |
|
|
|
PESSOA 16 |
|
|
|
PESSOA 17 |
|
|
|
PESSOA 18 |
|
|
|
PESSOA 19 |
|
|
|
A área de dados armazenada na memória é a área mais clara da tabela.
O acesso aos dados da tabela é feito da seguinte maneira:
data[linha][coluna]
Exemplo para o dia da pessoa 14:
printf("%d", data[14][0]);
Atribuição inicial
Assim como nos vetores, podemos atribuir os valores iniciais em uma matriz. Exemplo:
#include <stdio.h>
int tabela[3][2] = { {1,2},{3,4},{5,6} };
int i,j;
main()
{
for (i=0; i<3; i++)
{
for (j=0; j<2; j++)
printf("%d ",tabela[i][j]);
printf("\n");
}
}
Saída:
1 2
3 4
5 6
A hirearquia das chaves na declaração é: coluna (mais interno), depois a linha (mais externo).
Algumas considerações
Só podemos criar listas ou tabelas com o mesmo tipo de variável (int, float, char, etc). Para utilizarmos múltiplos tipos de dados, devemos criar uma estrutura (struct).
O tamanho do vetor/tabela é fixo. Deve ser definido antes de rodar o programa, em sua fase de construção. Isto pode levar ao desperdício ou falta de espaço.
Quantas dimensões podemos criar a matriz?
D dimensões. Basta colocar a quantidade de colchetes correspondente ao número de dimensões. Exemplo para 3 dimensões:
int a [5][3][2];
Matriz de 3 dimensões criada:
+--+--+--+--+--+
+--+--+--+--+--+ |
| | | | | | +
+--+--+--+--+--+ |
| | | | | | +
+--+--+--+--+--+ |
| | | | | | +
+--+--+--+--+--+
Eu preciso usar o vetor / matriz todo?
Não. Você pode projetar um vetor com 1000 posições, mas necessitar usar apenas 20. Daí o desperdício de espaço.
Inicialização de vetores do tipo string
O vetor ou matriz pode ser inicializado, através de chaves e os elementos separados por vírgulas. Exemplo:
char *nome[5] = { "Penguin", "Galious", "Noriko", "Matchday", "Konami" };
char nome[2][10] = {"Beatriz", "Ana" };
A declaração char *nome[5] inidica um vetor de 5 posições, com strings de tamanho N (guarda 5 nomes).
Já a declaração char nome[2][10] inidica um vetor de 2 posições, com strings de tamanho 10 (guarda 2 nomes com no máximo 10 letras).
Inicialização de vetores de tamanho N
É possível declarar um vetor de tamanho igual a N, sendo o valor de N estabelecido pelo número de elementos declarados na inicialização. Para isso, basta utilizar os colchetes vazios. Exemplo:
int v[] = { 1,2,3,4,5,6 };
O vetor "v" terá tamanho igual a 6.
O mesmo pode ser aplicado às strings:
char *nome[] = { "Penguin", "Galious", "Noriko", "Matchday", "Konami" };
Esse tipo de declaração OBRIGA a inserção de valores iniciais, sob pena de dar erro. Exemplo:
char *nome[]; // Erro. Não foi iniciada!
Utilizando "variáveis" para determinar o tamanho de vetores e matrizes
Suponhamos que nosso programa necessite da criação de alguns vetores e/ou matrizes que possam ter suas dimensões facilmente alteradas em tempo de compilação. Entretanto, a linguagem C não permite a declaração de um vetor ou matriz utilizando variáveis. Veja o exemplo a seguir.
#include <stdio.h>
int n=15;
double vetor[n];
double matriz[n][4];
main()
{
vetor[0] = 1;
}
Ao tentarmos compilar o código acima, o compilador retorna um erro.
Para resolver esse problema, podemos utilizar a diretiva #define (vide capítulo 2).
#include <stdio.h>
#define n 15
double vetor[n];
double matriz[n][4];
main()
{
vetor[0] = 1;
}
Agora, o programa compila com sucesso.
Observe que apesar de n possuir valor fixo ao compilarmos o programa, podemos alterar o valor dele no código, impactando em todos os vetores e matrizes a ele associados. Isto significa que, se eu necessitar aumentar o tamanho de vetor ou matriz, não preciso mais realizar alterações de dimensão em cada um deles, mas sim na definição de n.
Você deve estar se perguntando: como eu faço para criar um vetor ou matriz redimensionável em tempo de execução (quando o programa está rodando)?
Podemos resolver isso de duas maneiras: uma, através de vetores e matrizes, que será abordada na próxima seção. A outra é utilizar variáveis dinâmicas através de ponteiros. Essa solução é mais eficiente, porém mais difícil de se implementar.
Listas de tamanho variável
Já sabemos que o vetor não permite o redimensionamento de seu tamanho em tempo de execução. Entretanto, é possível criar listas com o tamanho variável. Para isso, devemos criar um vetor com o tamanho máximo previsto para essa lista e utilizar somente a quantidade de campos necessários.
Principais características:
- Uma variável irá controlar a quantidade de elementos presentes na lista.
- A parte da lista utilizada vai de 0 a quantidade-1.
- Os elementos deverão estar dispostos de forma contígua. Assim, não deverão haver espaços em branco.
- A inserção e remoção de elementos deverá deslocar os demais elementos da lista.
Inserção
Há dois modos de inserção: o primeiro insere sempre ao final da lista, não sendo necessário o deslocamento dos elementos, enquanto que o segundo insere o elemento em uma posição P da lista, sendo necessário o deslocamento dos outros elementos, uma vez que não podemos sobrescrever sobre uma informação que já ocupa a posição P.
Exemplo de inserção do valor "7" ao final da lista:
tamanho = 3
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│ 4 │ 5 │ 9 │xxx│xxx│xxx│xxx│xxx│xxx│xxx│
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
0 1 2 3 4 5 6 7 8 9
Insere:
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│ 4 │ 5 │ 9 │ 7 │xxx│xxx│xxx│xxx│xxx│xxx│
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
0 1 2 3 4 5 6 7 8 9
tamanho = 4
Exemplo de inserção do valor "7" na posição "1" da lista:
tamanho = 3
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│ 4 │ 5 │ 9 │xxx│xxx│xxx│xxx│xxx│xxx│xxx│
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
0 1 2 3 4 5 6 7 8 9
Desloca (abre espaço):
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│ 4 │ 5 │ 5 │ 9 │xxx│xxx│xxx│xxx│xxx│xxx│
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
0 1 2 3 4 5 6 7 8 9
↑
Insere:
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│ 4 │ 7 │ 5 │ 9 │xxx│xxx│xxx│xxx│xxx│xxx│
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
0 1 2 3 4 5 6 7 8 9
↑
tamanho = 4
O deslocamento DEVERÁ sempre começar pelo último elemento. Se fosse começado pelo primeiro elemento a ser deslocado, ele sobrescreveria o próximo elemento.
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│ 4 │ 5 │ 9 │xxx│xxx│xxx│xxx│xxx│xxx│xxx│
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
0 1 2 3 4 5 6 7 8 9
Procedimento correto:
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│ 4 │ 5 │ 9 │ 9 │xxx│xxx│xxx│xxx│xxx│xxx│
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
0 1 2 3 4 5 6 7 8 9
---->
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│ 4 │ 5 │ 5 │ 9 │xxx│xxx│xxx│xxx│xxx│xxx│
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
0 1 2 3 4 5 6 7 8 9
---->
Procedimento Errado:
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│ 4 │ 5 │ 5 │xxx│xxx│xxx│xxx│xxx│xxx│xxx│
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
0 1 2 3 4 5 6 7 8 9
---->
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│ 4 │ 5 │ 5 │ 5 │xxx│xxx│xxx│xxx│xxx│xxx│
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
0 1 2 3 4 5 6 7 8 9
---->
Programa exemplo, que possui as ferramentas de inserção ao final (push) e inserção em qualquer posição.
#include <stdio.h>
int v[10] = { 4,5,9,0,0,0,0,0,0,0 };
int total=3;
void push(int valor)
{
if (total>= 10)
return;
v[total] = valor;
total++;
}
void insere(int valor, int pos)
{
int i;
if ((total>= 10) || (pos < 0) || (pos > total))
return;
for (i=total-1; i>=pos; i--)
v[i+1] = v[i];
v[pos] = valor;
total++;
}
void imprime()
{
int i;
for (i=0; i<total; i++)
printf("%d ", v[i]);
printf("\n");
}
main()
{
imprime();
push(7);
imprime();
insere(6,1);
imprime();
}
Saída:
4 5 9
4 5 9 7
4 6 5 9 7
Remoção
Há dois modos de remoção: o primeiro remove sempre ao final da lista, não sendo necessário o deslocamento dos elementos, enquanto que o segundo remove o elemento em uma posição P da lista, sendo necessário o deslocamento dos outros elementos.
Exemplo de remoção do último elemento da lista:
tamanho = 4
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│ 4 │ 5 │ 9 │ 7 │xxx│xxx│xxx│xxx│xxx│xxx│
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
0 1 2 3 4 5 6 7 8 9
↑
Remove:
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│ 4 │ 5 │ 9 │ 7 │xxx│xxx│xxx│xxx│xxx│xxx│
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
0 1 2 3 4 5 6 7 8 9
↑
tamanho = 3
Exemplo de remoção do item da posição "1" da lista:
tamanho = 3
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│ 4 │ 5 │ 9 │xxx│xxx│xxx│xxx│xxx│xxx│xxx│
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
0 1 2 3 4 5 6 7 8 9
↑
Desloca:
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│ 4 │ 9 │ 9 │xxx│xxx│xxx│xxx│xxx│xxx│xxx│
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
0 1 2 3 4 5 6 7 8 9
↑
tamanho = 2
Em ambos os casos, o "lixo" permanece na antiga última posição. Entretanto, o tamanho da lista diminuiu e esse "lixo" é ignorado.
Diferente da inserção, o deslocamento agora deverá começar pelo primeiro elemento a ser deslocado.
Adicionar ao programa anterior:
void pop()
{
if (total<=0)
return;
total--;
}
void remover(int pos)
{
int i;
if ((total<= 0) || (pos < 0) || (pos >= total))
return;
for (i=pos+1; i<total; i++)
v[i-1] = v[i];
total--;
}
E na função "main()":
pop();
imprime();
remover(0);
imprime();
Saída:
4 5 9
4 5 9 7
4 6 5 9 7
4 6 5 9
6 5 9