Вы находитесь на странице: 1из 10

Universidade Estadual do Maranhão

Curso de Engenharia da Computação


Mestrado em Engenharia da Computação e Sistemas

Notas de Aulas Vetores e Matrizes


Prof. Dr. Luís Carlos Costa Fonseca

São Luís – 2020

Endereços e Ponteiros
Universidade Estadual do Maranhão
Curso de Engenharia da Computação
Mestrado em Engenharia da Computação e Sistemas

Os conceitos de endereço e ponteiro são fundamentais em qualquer linguagem de


programação, embora sejam mais visíveis em C que em outras linguagens. O conceito de
ponteiro é difícil; para dominá-lo, é preciso fazer certo esforço.

Endereços

A memória de qualquer computador é uma sequência de bytes. Cada byte armazena


256 possíveis valores. Os bytes são numerados sequencialmente e o número de um byte é o
seu endereço.

Cada objeto na memória do computador ocupa um certo número de bytes


consecutivos. No meu computador, um char ocupa 1 byte, um int ocupa 4 bytes e um double
ocupa 8 bytes. Cada objeto na memória do computador tem um endereço (Na maioria dos
computadores, o endereço de um objeto é o endereço do seu computador).

Em C, o endereço de um objeto é dado pelo operador &. Não confunda este & com o
operador lógico and, representado por &&. Assim, se i é uma variável então &i é o seu
endereço.

O operador & aparece frequentemente nas invocações da função scanf, por exemplo.
Se i é uma variável inteira, podemos escrever scanf(“%d”, &i).

Ponteiros

Um ponteiro (ou apontador) é um tipo especial de variável destinado a armazenar


endereços. Todo ponteiro pode ter o valor “NULL”, que é um endereço inválido. A constante
NULL está definida no arquivo-interface stdlib e seu valor é 0 na maioria dos computadores.

Se um ponteiro p armazena o endereço de uma variável i, podemos dizer “p aponta


para i” ou “p é o endereço de i”. Se um ponteiro p tem valor diferente de NULL então *p é o
valor do objeto apontado por p. Não confunda esse uso de * com o operador de multiplicação!
Por exemplo, se i é uma variável e p = &i então escrever “*p” é o mesmo que escrever ”i”.

Há vários tipos de ponteiros: ponteiros para caracteres, ponteiros para inteiros,


ponteiros para registros, ponteiros para ponteiros inteiros etc. para declaras um ponteiro p
para um inteiro, escreva:

int *p;

Para declarar um ponteiro q para um registro cel, escreva:


Universidade Estadual do Maranhão
Curso de Engenharia da Computação
Mestrado em Engenharia da Computação e Sistemas

struct cel *q;

Exercícios

1. Se i é uma variável do tipo int, que sentido fazem as expressões *&i e &&i ?
2. Leia o verbete Poiter na Wikipedia.
3.

Uma Aplicação

Suponha que precisamos de uma funções que troque os valores de duas variáveis
inteiras i e j. A função

void troca(int i, int j){

int temp;

temp = i; i = j; j = temp;

Não produz o efeito desejado, pois recebe apenas os valores das variáveis e não as
variáveis propriamente ditas. Para obter o efeito desejado, é preciso passar à função os
endereços das variáveis:

void troca(int *p, int *q){

int temp;

temp = *p; *p = *q; *q = temp;

Para aplicar a função às variáveis i e j basta escrever:

troca(&i , &j);

Exercícios

1. Um ponteiro pode ser usado para dizer a uma função onde ela deve depositar o
resultado de seus cálculos. Escreva uma função hm que converta minutos em horas-e-
minutos. A função recebe um inteiro t e os endereços de duas variáveis inteiras,
Universidade Estadual do Maranhão
Curso de Engenharia da Computação
Mestrado em Engenharia da Computação e Sistemas

digamos h e m, e atribui valores não negativos a essas variáveis de modo que


tenhamos m < 60 e 60h + m = t. Escreva também uma função main que use a função
hm.
2. Escreva uma função mm que receba um vetor inteiro v[0..n-1] e os endereços de duas
variáveis inteiras, digamos min e max, e deposite nestas variáveis o valor de um
elemento mínimo e o valor de um elemento máximo do vetor. Escreva também uma
função main que use a função mm.

Vetores

O tipo vetor permite armazenar mais de um valor em uma mesma variável. O tamanho
dessa variável é definido na sua declaração, e seu conteúdo é dividido em posições. Nessa
estrutura todos os elementos são do mesmo tipo, e cada um pode receber um valor diferente.

Algumas características do tipo vetor:

 Alocação estática (deve-se conhecer as dimensões da estrutura no momento da


declaração)
 Estrutura homogênea
 Alocação seqüencial (bytes contíguos)
 Inserção/Exclusão
o Realocação dos elementos
o Posições de memória não liberadas

A partir do endereço do primeiro elemento é possível determinar a localização dos


demais elementos do vetor. Isso é possível porque os elementos do vetor estão dispostos na
memória um ao lado do outro.

1. Elemento[1] = endereço inicial


2. Elemento[2] = endereço inicial + tamanho elemento
3. Elemento[3] = endereço inicial + 2 * tamanho elemento

Exemplo:

int v[5]

Vetores e Endereços
Universidade Estadual do Maranhão
Curso de Engenharia da Computação
Mestrado em Engenharia da Computação e Sistemas

Os elementos de qualquer vetor são alocados consecutivamente na memória do


computador. Se cada elemento ocupa l bytes, a diferença entre os endereços de dois
elementos consecutivos será l. Para livrar o programador da preocupação com o valor de l, o
compilador C cria a ilusão de que l vale 1 qualquer que seja o tipo dos elementos do vetor. Por
exemplo, depois dos comandos:

int *v;

v = malloc (100 * sizeof(int));

O ponteiro v aponta o primeiro elemento de um vetor de 100 inteiros. O endereço do


segundo elemento do vetor v+1 e o endereço do terceiro é v+2. Se i é uma variável do tipo int
então v+1 é o endereço do (i+1)-ésimo elemento do vetor.

A propósito, as expressões v+i e &v[i] têm exatamente o mesmo valor e portanto as


atribuições:

*(v+i) = 987; //e

v[i] = 987;

Têm o mesmo efeito. Todas estas considerações também valem se o vetor tiver sido
alocado estaticamente, por uma declaração como:

int v[100];

Mas nesse caso v é uma espécie de “ponteiro constante”, cujo valor não pode ser
alterado.

Qualquer dos dois fragmentos de código abaixo pode ser usado para “carregar” o
vertor v[0..99].

for (i = 0; i < 100; i++) scanf(“%d”, v + i);

for (i = 0; i < 100; i++) scanf(“%d”, &v[i]);

Exercícios

1. Seja v um vetor de int’s. suponha que cada int ocupa 8 bytes no seu computador. Se o
endereço de v[0] é 55000, qual o valor da expressão v + 3?
2. Se v é um vetor, qual a diferença conceitual entre as expressões v[3] e v + 3?
3. Suponha que o vetor v e a variável k foram declarados assim:

int v[100], k;
Universidade Estadual do Maranhão
Curso de Engenharia da Computação
Mestrado em Engenharia da Computação e Sistemas

Descreva, em português, a sequência de operações que deve ser executada para


calcular o valor da expressão &v[k+9].

Matrizes

Uma matriz é um arranjo bidimensional ou multidimensional de alocação estática e


seqüencial. Todos os valores da matriz são do mesmo tipo e tamanho, e a posição de cada
elemento é dada pelos índices, um para cada dimensão.

Os elementos ocupam posições contíguas na memória. A alocação dos elementos da


matriz na memória pode ser feita colocando os elementos linha-porlinha ou coluna-por-
coluna.

Definição:

int matriz[2][2];

Definições sobre matrizes:

 Estrutura homogênea;
 Arranjo bi ou multidimensional (um índice por dimensão);
 Alocação estática;
 Posições contíguas de memória;

Exemplo:

Matrizes Especiais

Uma matriz é especial quando a maioria de seus elementos é igual a zero ou a um


valor constante. Nesse tipo de matriz ocorre um desperdício de espaço na alocação de
memória porque a maioria dos elementos tem o mesmo valor. Para solucionar o problema de
Universidade Estadual do Maranhão
Curso de Engenharia da Computação
Mestrado em Engenharia da Computação e Sistemas

desperdício de memória, podemos representar apenas os valores significativos (diferentes de


zero ou valor constante) da matriz.

Matrizes Diagonais

Nesse tipo de matriz apenas os elementos da diagonal principal ou secundária são


significativos, os demais elementos são iguais a uma constante. Nesse caso, a matriz pode ser
representada por um vetor V [1..L] (onde L é igual ao número de linhas da matriz), contendo os
valores significativos da diagonal principal ou secundária.

Ex.: Matriz 4 x 4:

Matrizes Triangulares

Nas matrizes triangulares apenas os elementos da diagonal principal ou secundária e


os abaixo (ou acima) têm valor diferente da constante ou zero. Neste caso, também vamos
armazenar apenas os valores significativos em um vetor.

Ex.:

Função de mapeamento para uma matriz triangular inferior:

M[i,j] = v[j + i * (i − 1) / 2]

Matrizes Simétricas e Anti-simétricas


Universidade Estadual do Maranhão
Curso de Engenharia da Computação
Mestrado em Engenharia da Computação e Sistemas

Uma matriz é simétrica quando M[i,j] = M[j,i] e é anti-simétrica quando M[i,j] = -M[j,i].
Neste caso, é necessário representar apenas os elementos do triângulo superior ou inferior
pelo mesmo método usado para matrizes triangulares.

Ex.:

Exercícios

1. Escreva uma função que receba uma matriz como entrada e retorne em um vetor a
sua diagonal principal;
2. Escreva uma função que receba uma matriz como entrada e retorne em um vetor a
sua diagonal secundaria;
3. Escreva uma função que receba uma matriz como entrada e retorne em um vetor os
elementos da matriz triangular que estão abaixo da diagonal principal;
4. Escreva uma função que receba uma matriz como entrada e retorne em um vetor os
elementos da matriz triangular que estão acima da diagonal principal;
5. Escreva uma função que receba uma matriz como entrada e retorne em um vetor os
elementos da matriz triangular que estão abaixo da diagonal secundaria;
6. Escreva uma função que receba uma matriz como entrada e retorne em um vetor os
elementos da matriz triangular que estão acima da diagonal secundaria;
7. Escreva uma função que verifique se uma matriz é simétrica;
8. Escreva uma função que verifique se uma matriz é anti-simétrica;

Matrizes Esparsas

Em uma matriz esparsa, a maioria dos elementos é igual a zero, sendo apenas 30% dos
valores significativos. Para esse tipo de estrutura também armazenam-se apenas os valores
significativos. Veja a figura abaixo:

Uma matriz é dita esparsa quando possui uma grande quantidade de elementos que
valem zero (ou não presentes, ou não necessários). Matrizes esparsas têm aplicações em
Universidade Estadual do Maranhão
Curso de Engenharia da Computação
Mestrado em Engenharia da Computação e Sistemas

problemas de engenharia, física (por exemplo, o método das malhas para resolução de
circuitos elétricos ou sistemas de equações lineares). Também têm aplicação em computação:
armazenamento de dados (e.g., planilhas eletrônicas)

A matriz esparsa é implementada através de um conjunto de listas ligadas que


apontam para elementos diferentes de zero. De forma que os elementos que possuem valor
zero não são armazenados. Abaixo segue uma possível definição

struct Celula {

Celula direita, abaixo;

int linha, coluna;

double valor;

Uma implementação mais simples seria por meio de um arranjo com a seguinte
estrutura:

struct Celula {

int linha, coluna;

double valor;

Exercícios

1. Sobre Matrizes Esparsas (ME)


a. Quais suas vantagens
b. Quais suas desvantagens
c. Dê exemplos de uso
d. Defina vetor esparso, defina tanto sua declaração como sua funcionalidade.
e. Quando usar uma ME
2. Faça algoritmos que:
a. Leia e imprima uma matriz esparsa
b. Some os elementos da linha i a uma constante k
c. Some duas matrizes esparsas
d. Verifique se uma matriz esparsa é a identidade
e. Multiplique um vetor esparso por uma ME
Universidade Estadual do Maranhão
Curso de Engenharia da Computação
Mestrado em Engenharia da Computação e Sistemas

f. Multiplique duas matrizes esparsas


g. Calcule a inversa de uma matriz esparsa

Вам также может понравиться