Mostrando postagens com marcador matriz. Mostrar todas as postagens
Mostrando postagens com marcador matriz. Mostrar todas as postagens

terça-feira, 12 de janeiro de 2010

Matrizes. Final (Matriz de strings/alocação)

Hoje, para encerrar a nossa série sobre matrizes, vamos ver a matriz de strings.

Matriz de Strings
Uma matriz de string é uma matriz de caracteres de duas dimensões, onde a 1a dimensão é a quantidade de strings e a 2a dimensão é o tamanho máximo de cada string.

 char matrstr[5][30];

Na linha acima, definimos uma matriz capaz de armazenar 5 strings que tenham no máximo 29 caracteres cada (o último caracter é o \0, o finalizador da string).
Se quisermos nos referir a cada string individualmente, é só nos referir a uma única dimensão.

 printf("%s\n", matrstr[2]);

O comando acima exibe a 3a string da matriz.

Vejamos um código para exemplificar isto.


#include <stdio.h>
#include <stdlib.h>
const int QTDSTR = 5;
const int COMPRIMENTO = 30;
 
void preenche_matrstr(char matriz[][30]);
 
int main (){
 char matrstr [5][30];
 int i, opcao;
 
 preenche_matrstr(matrstr);
 do{
    printf("Escolha uma das opcao abaixo:\n\n");
    printf("1. Exibir posicoes pares\n");
    printf("2. Exibir posicoes impares\n");
    printf("3. Sair\n\nopcao: ");
    scanf("%d", &opcao);
    switch(opcao){
       case 1:
          for(i = 0; i < QTDSTR; i++)
             if(!((i+1)%2))
                printf("palavra %d: %s\n", i+1, matrstr[i]);
          break;
       case 2:
          for(i = 0; i < QTDSTR; i++)
             if((i+1)%2)
                printf("palavra %d: %s\n", i+1, matrstr[i]);
          break;
       case 3:
          break;
       default:
          printf("OPCAO INVALIDA!!! Digite novamente.\n");
    }
    system("pause");
    system("cls");
 }while(opcao != 3);
}
 
void preenche_matrstr(char matriz[][30]){
 int i;
 
 printf("PREENCHENDO UMA MATRIZ DE STRINGS:\n\n");
 for(i = 0; i < QTDSTR; i++){
    printf ("Digite uma palavra para a posicao %d: ", i+1);
    gets(matriz[i]);
 }
 system("cls");
}


Este código preenche uma matriz de strings com palavras digitadas pelo usuário, e depois apresenta um menu onde ele escolhe se deseja ver as palavras nas posições pares ou ímpares.
As matrizes de strings são conceitos muito simples. Mas que podem ficar muito complicados se incluirmos ponteiros na brincadeira.

Ponteiros x Matrizes
Você deve lembrar de quando apresentei o conceito de matriz (aqui), como sendo um vetor de vetores.
Também deve estar lembrado sobre a forma como alocamos um vetor dinamicamente usando a função malloc e um ponteiro (aqui). Portanto, a declaração de um vetor com o comando abaixo é muito comum:

 char* str;

E o que você diria ao se deparar com a declaração abaixo:

 char** matrstr;

Ela assusta no início. Mas depois que conhecemos o seu significado fica fácil (assim eu espero rsrs).

Apontando para quem já está apontando
O comando acima declara um ponteiro para um ponteiro de char (???). Antes que você pense que usando ele estamos declarando uma matriz, eu já quero deixar bem claro que não. O comando acima não declara uma matriz.

Observe o código abaixo:


#include <stdio.h>
#include <stdlib.h>
 
int main(){
 char** matriz;
 char matrstr[5][30];
 
 printf("O tamanho de char* matriz eh %d.\n", sizeof(matriz));
 printf("O tamanho de char matrstr[1][1] eh %d.\n", sizeof(matrstr));
 system("pause");
}


Como demonstrado no código acima, ao receber uma declaração de um ponteiro para ponteiro (ou mesmo um ponteiro simples), o compilador reversa espaço para armazenar um endereço (aqui na minha máquina esse espaço é de 4 bytes). Já quando recebe a declaração de uma matriz, ele reserva espaço para toda a matriz. Neste caso especificamente, são reservados 150 bytes.
Mas, assim como podemos tratar um endereço para um bloco de memória como um vetor, podemos fazer o mesmo com uma matriz, como inclusive já vimos na 2a parte desta série. (A diferença no post de hoje é que vamos expandir um pouco mais o conceito.)
Para isso, devemos alocar dinamicamente um bloco de memória com tamanho suficiente para armazenar nossa matriz inteira, e depois alocar, dentro deste bloco, os espaços para conter cada 'linha' da nossa matriz individualmente.
Vamos ao código:


#include <stdio.h>
#include <stdlib.h>
 
char** preenche_matrstr(int*, int*);
 
int main (){
 char **matrstr;
 int qtdstr, tamanho, i, opcao;
 
 matrstr = preenche_matrstr(&qtdstr, &tamanho);
 do{
    printf("Escolha uma das opcao abaixo:\n\n");
    printf("1. Exibir posicoes pares\n");
    printf("2. Exibir posicoes impares\n");
    printf("3. Sair\n\nopcao: ");
    scanf("%d", &opcao);
    switch(opcao){
       case 1:
          for(i = 0; i < qtdstr; i++)
             if(!((i+1)%2))
                printf("palavra %d: %s\n", i+1, *(matrstr+i));
          break;
       case 2:
          for(i = 0; i < qtdstr; i++)
             if((i+1)%2)
                printf("palavra %d: %s\n", i+1, matrstr[i]);
          break;
       case 3:
          break;
       default:
          printf("OPCAO INVALIDA!!! Digite novamente.\n");
    }
    system("pause");
    system("cls");
 }while(opcao != 3);
 for(i = 0; i < *qtd; i++){
    free(matrstr[i]);
 free(matrstr);
}
 
char** preenche_matrstr(int* qtd, int* tam){
 char** matriz;
 int i;
 
 printf("Digite a quantidade de strings da matriz: ");
 scanf("%d", qtd);
 printf("Digite o tamanho maximo de cada string: ");
 scanf("%d", tam);
 
 matriz = (char **) malloc (*qtd);
 if (!matriz){
    printf("NAO FOI POSSIVEL ALOCAR A MATRIZ.\n");
    exit(0);
 }
 else
    for(i = 0; i < *qtd; i++){
       *(matriz + i) = (char*) malloc (*tam);
       if (!(*(matriz + i))){
          printf("NAO FOI POSSIVEL ALOCAR A STRING %d.\n", i+1);
          exit(i);
       }
    }
 
 printf("PREENCHENDO UMA MATRIZ DE STRINGS:\n\n");
 setbuf(stdin, NULL);
 for(i = 0; i < *qtd; i++){
    printf ("Digite uma palavra para a posicao %d: ", i+1);
    gets(matriz [i]);
 }
 system("cls");
 return matriz;
}


Como a matriz será armazenada dinamicamente, demos ao usuário a possibilidade de escolher quantas serão e qual o tamanho máximo das strings da matriz.
Observe que em alguns pontos do código eu usei matriz[i] e em outros eu usei *(matriz+i). Foi simplesmente para mostrar que os 2 são equivalentes. Não é prático, misturar duas notações diferentes em um mesmo código. Escolha uma delas e use em todo o código.

OBS: Existe um comando novo neste código (setbuf), mas não comentarei sobre ele agora. No próximo post falaremos detalhadamente sobre ele.

Até o próximo post.

domingo, 10 de janeiro de 2010

Matrizes. Parte III (transposta/determinante)

Agora que já conhecemos as matrizes, vamos ver algumas operações com matrizes.

Transpondo uma matriz quadrada
Vamos começar criando uma matriz transposta de uma matriz quadrada 3x3. Não sabe o que é matriz transposta? Veja aqui.
Olhando a primeira vista podemos pensar que seria só trocar a posição dos elementos. Algo que o fragmento de código abaixo faria sem problemas.


 for(i = 0; i < colunas; i++)
    for(j = 0; j < colunas; j++){
       aux = mat[i][j];
       mat[i][j] = mat[j][i];
       mat[j][i] = aux;
 }


Se você tiver a curiosidade pode implementar este código. E vai ter uma triste surpresa (rsrs).
Será que você pode dizer por que este código não funciona?

Como você já descobriu, este código funciona (compila e executa), mas não faz o que se esperava que fizesse. Na verdade, ele troca a posição dos elementos do vetor e, depois, os retorna para as posições iniciais. Ele não sabe quando os elementos não precisam ser trocados, ou porque já foram, ou porque realmente não é necessário, como é o caso dos elementos da diagonal principal da matriz.
No caso da diagonal principal é fácil, basta saber se o número da coluna é igual ao da linha. E no outros casos? Como evitar que a troca seja feita novamente, fazendo o elemento voltar para a sua posição inicial?
Vamos fazer um teste de mesa (ou chinês, como preferirem) e ver como as trocas são feitas.
Linha = 0, Coluna = 0 => Diagonal principal, não precisa trocar;
Linha = 0, Coluna = 1 => Troca o elemento com o da Linha = 1, Coluna = 0;
Linha = 0, Coluna = 2 => Troca o elemento com o da Linha = 2, Coluna = 0;

Linha = 1, Coluna = 0 => A troca já foi feita, não precisa trocar;
Linha = 1, Coluna = 1 => Diagonal principal, não precisa trocar;
Linha = 1, Coluna = 2 => Troca o elemento com o da Linha = 2, Coluna = 1;
Linha = 2, Coluna = 0 => A troca já foi feita, não precisa trocar;
Linha = 2, Coluna = 1 => A troca já foi feita, não precisa trocar;
Linha = 2, Coluna = 2 => Diagonal principal, não precisa trocar;

Observando as linhas em negrito, que mostram as trocas que precisam ser realmente feitas, podemos encontrar algo em comum entre elas. Já percebeu?

O número da linha é menor que o da coluna! Agora ficou fácil, né?
Veja abaixo como fica o código completo que faz a transposição de uma matriz quadradra 3x3.


#include <stdio.h>
#include <stdlib.h>
 
const int LINHAS = 3;
const int COLUNAS = 3;
 
void preenche_mat_int(int [][3]);
void exibe_mat_int(int [][3]);
void transpor_mat_int(int [][3]);
 
int main(){
 int matriz[3][3];
 
 preenche_mat_int(matriz);
 exibe_mat_int(matriz);
 transpor_mat_int(matriz);
 exibe_mat_int(matriz);
 system("pause");
}
 
int ler_int(){
 int digitado;
 
 printf("Digite um valor inteiro: ");
 scanf("%d",&digitado);
 return digitado;
}
 
void mostra_int(int num){
 printf("%d ",num);
}
 
void preenche_mat_int(int mat[][3]){
 int i, j;
 
 printf("\nPREENCHENDO A MATRIZ:\n");
 for(i = 0; i < LINHAS; i++)
    for(j = 0; j < COLUNAS; j++)
       mat[i][j] = ler_int();
}
 
void exibe_mat_int(int mat[][3]){
 int i, j;
 
 printf("\n");
 for(i = 0; i < LINHAS; i++){
    for(j = 0; j < COLUNAS; j++)
       mostra_int(mat[i][j]);
    printf("\n");
 }
 printf("\n\n");
}
 
void transpor_mat_int(int mat [][3]){
 int i, j, aux;
 
 printf("\nTRANSPOSICAO DA MATRIZ:\n");
 for(i = 0; i < LINHAS; i++)
    for(j = 0; j < COLUNAS; j++)
       if((i != j) && (i < j)){
          aux = mat[i][j];
          mat[i][j] = mat[j][i];
          mat[j][i] = aux;
       }
}


Determinante de uma matriz quadrada 2x2
Outra função comum no trabalho com matrizes é o cálculo do seu determinante. Veja o que é, e como é feito aqui.
O código abaixo calcula o determinante de uma matriz quadrada 2x2.


#include <stdio.h>
#include <stdlib.h>
 
const int LINHAS = 2;
const int COLUNAS = 2;
 
void preenche_mat_int(int [][2]);
void exibe_mat_int(int [][2]);
int determ_mat_int_2x2(int [][2]);
 
int main(){
 int matriz[2][2], det;
 
 preenche_mat_int(matriz);
 exibe_mat_int(matriz);
 det = determ_mat_int_2x2(matriz);
 printf("O determinante da matriz informada = %d\n", det);
 system("pause");
}
 
int ler_int(){
 int digitado;
 
 printf("Digite um valor inteiro: ");
 scanf("%d",&digitado);
 return digitado;
}
 
void mostra_int(int num){
 printf("%d ",num);
}
 
void preenche_mat_int(int mat[][2]){
 int i, j;
 
 printf("\nPREENCHENDO A MATRIZ:\n");
 for(i = 0; i < LINHAS; i++)
    for(j = 0; j < COLUNAS; j++)
       mat[i][j] = ler_int();
}
 
void exibe_mat_int(int mat[][2]){
 int i, j;
 
 printf("\n");
 for(i = 0; i < LINHAS; i++){
    for(j = 0; j < COLUNAS; j++)
       mostra_int(mat[i][j]);
    printf("\n");
 }
 printf("\n\n");
}
 
int determ_mat_int_2x2(int mat[][2]){
 
 return (mat[0][0] * mat[1][1]) - (mat[0][1] * mat[1][0]);
}


É possível calcular o determinante de uma matriz quadrada de qualquer ordem, mas para isto precisamos ver um conceito que ainda não vimos. Voltarei a este assunto mais a frente.

Até o próximo post.

sábado, 9 de janeiro de 2010

Matrizes. Parte II (funções/alocação)

Continuando nossa série sobre matrizes, hoje vamos ver algumas operações básicas com matrizes.

Matrizes x Funções
Por serem intimamente relacionadas com os vetores, a operações entre matrizes e funções é praticamente idêntica, salvo o fato de termos uma (ou muitas) dimensão(ões) a mais.
Vamos ao código e você vai entender.


#include <stdio.h>
#include <stdlib.h>
 
const int LINHAS = 2;
const int COLUNAS = 2;
 
void preenche_mat_int(int [2][2]);
void exibe_mat_int(int [][2]);
 
int main(){
 int matriz[2][2];
 
 preenche_mat_int(matriz);
 exibe_mat_int(matriz);
 system("pause");
}
 
int ler_int(){
 int digitado;
 
 printf("Digite um valor inteiro: ");
 scanf("%d",&digitado);
 return digitado;
}
 
void mostra_int(int num){
 printf("%d ",num);
}
 
void preenche_mat_int(int mat[2][2]){
 int i, j;
 
 printf("\nPREENCHENDO A MATRIZ:\n");
 for(i = 0; i < LINHAS; i++)
    for(j = 0; j < COLUNAS; j++)
       mat[i][j] = ler_int();
}
 
void exibe_mat_int(int mat[][2]){
 int i, j;
 
 printf("\n");
 for(i = 0; i < LINHAS; i++){
    for(j = 0; j < COLUNAS; j++)
       mostra_int(mat[i][j]);
    printf("\n");
 }
 printf("\n\n");
}


O código acima tem bastante novidade!
A 1a delas é o uso de protótipos de função. Eu já deveria ter falado sobre eles, quando falei sobre funções (aqui), mas nunca é tarde (rsrs).

Faça sem saber como.
Em todos os códigos que vimos até agora usando funções, sempre criamos as funções antes de chamá-las na main. O código da nossa função main era sempre o último e, assim, as funções já eram 'conhecidas' dela.
No entanto é possível criar a main antes das outras funções. O único requisito é informar como essas funções serão chamadas, quais o seus retornos e parâmetros.
Esta é a função dos protótipos. Eles se parecem com o cabeçalho das funções, exceto que o nome dos parâmetros pode ser omitido.

A 2a novidade, que já aparece nos protótipos, é a forma de passar uma matriz para uma função. Deve ser informado o tipo de dados contido na matriz e o tamanho de suas dimensões. O tamanho da 1a dimensão pode ser omitido, somente da 1a. Se a matriz tiver 3 dimensões, por exemplo, o tamanho das 2a e 3a dimensões tem que ser informado.
A última é a forma de 'varrer' a matriz. Usamos um for para 'varrer' as linhas e, dentro dele, um outro for para 'varrer' todas as colunas desta linha antes de passar para a próxima. Por isso a necessidade de saber quantas linhas e colunas a matriz tem.
Como as dimensões da matriz já são conhecidas é uma ótima prática criar constantes globais que armazenem estes valores, o que facilita o acesso a elas em qualquer parte do programa.

Funções x Matrizes
Da mesma forma que um vetor pode ser alocado dinâmicamente, uma matriz também pode ser. Observe o código abaixo:


#include <stdio.h>
#include <stdlib.h>
 
int ler_int(){
 int digitado;
 
 printf("Digite um valor inteiro: ");
 scanf("%d",&digitado);
 return digitado;
}
 
void mostra_int(int num){
 printf("%d ",num);
}
 
int* preenche_mat_int(int linhas, int colunas){
 int *mat=0, i, j;
 
 if ((linhas * colunas) > 0)
    mat = (int *) malloc (linhas * colunas * sizeof(int));
 if (mat){
    printf("\nPREENCHENDO A MATRIZ:\n");
    for(i = 0; i < linhas; i++)
       for(j = 0; j < colunas; j++)
          *(mat + (i * colunas) + j) = ler_int();
 }
 else
    printf("NAO FOI POSSIVEL ALOCAR A MATRIZ!!!\n");
 return mat;
}
 
void exibe_mat_int(int* mat, int linhas, int colunas){
 int i, j;
 
 printf("\n");
 for(i = 0; i < linhas; i++){
    for(j = 0; j < colunas; j++)
       mostra_int(*(mat + (i * colunas) + j));
    printf("\n");
 }
 printf("\n\n");
}
 
int main(){
 int *matriz, linhas, colunas;
 
 printf("Digite o numero de linhas da matriz (maior que zero):\n");
 linhas = ler_int();
 printf("Digite o numero de colunas da matriz(maior que zero):\n");
 colunas = ler_int();
 matriz = preenche_mat_int(linhas, colunas);
 if(matriz){
    exibe_mat_int(matriz, linhas, colunas);
    free(matriz);
 }
 system("pause");
}


A diferença básica neste código é a permitir ao usuário que defina o número de linhas e colunas da matriz, por isto não pudemos armazenar estes valores em constantes globais (observe que tivemos que passar estes valores para todas as funções). Uma vez definidas, linhas e colunas, verificamos se os valores informados foram válidos e, alocamos espaço para a matriz.
Como o bloco de memória alocado não foi definido como uma matriz, ele não pode ser indexado como uma, usando os []. A solução para isso é o uso de ponteiros e da aritmética de ponteiros.
A linha 25 mostra como isto é feito. Observe a multiplicação de i, que contém o número da linha atual, pela quantidade de colunas. Dá pra entender o porquê, certo?

Por hoje é isto. Até o próximo post.

sexta-feira, 8 de janeiro de 2010

Matrizes. Parte I (conceitos)

Hoje vamos começar a falar sobre um tipo de vetor especial: um vetor de vetores, a matriz.

Vetor de vetores?
Como já sabemos, vetor é um agrupamento sequencial de variáveis do mesmo tipo. O mais comum deles é um deste tipo que você está lendo aqui: um vetor de caracteres (as famosas strings). Confirmando o conceito: várias letras (olha o mesmo tipo aí) em sequência (olha a sequência aí). Podemos ter vetores de vários tipos de dados, inclusive de outros vetores.
Vou desenhar, assim você vai entender melhor.



Na figura acima, temos a representação de como é armazenado em memória um vetor que está armazenando outros 4 vetores.
Na verdade, esta divisões internas não existem(???). Este vetor de vetores, corresponde a um único vetor com 16 elementos. Então qual a razão dessa divisão?
A resposta é PRATICIDADE.
Lembra do lema 'Dividir para conquistar' que vimos aplicado às funções? Esta é a sua aplicação nos vetores. É muito mais fácil, visualizar cada pequeno bloco de cada vez do que todo o conjunto. Tanto é assim, que apesar deste vetor ser armazenado como na figura acima, nos referimos a ele, como se estivesse armazenado como na figura abaixo:



Agora passamos a enxergar o nosso vetor de vetores na forma de linhas, onde cada 'linha' armazena um vetor.
Olhando para esta representação, podemos notar que os elementos dos vetores armazenados nas 'linhas', parecem formar 'colunas'. Então, com o intuito de, mais uma vez, facilitar nossa visualização, chegamos a representação do nosso vetor de vetores de uma forma, espero, conhecida: a matriz. Observe a figura abaixo:



Esta é uma matriz de duas dimensões (linhas e colunas, ou eixos x e y). Não existem limites para o número de dimensões que uma matriz possa ter, embora quanto mais dimensões, mais trabalhosa é a operação com a matriz. As mais comuns são as de duas e três dimensões (páginas, linhas e colunas, ou eixos x, y e z).
Uma representação de uma matriz de 3 dimensões seria como na figura abaixo:



Agora que já vimos como uma matriz é armazenada e representada, vamos ver como é o seu uso em C.

As matrizes e o C
Para definir uma matriz em C, devemos informar o seu tipo, nome e os tamanhos de cada uma das suas dimensões.

 <tipo> nome [tamanho_dimensão1] [tamanho_dimensão2]...[tamanho_dimensãoN];

Para a matriz representada pelas figuras acima, uma matriz com 4 linhas e 4 colunas, que é do tipo inteiro, por exemplo, o comando seria:

  int matriz [4][4];

Assim, para atribuir o valor do elemento da 2a linha e 3a coluna, por exemplo, a uma variável inteira num, o comando é:

  num = matriz[1][2];

Assim como os vetores, o índice base de uma matriz também é 0 (zero). Então, a 2a linha é a de índice 1, e a 3a coluna é a de índice 2.

Fácil, não.
No próximo post veremos algumas operações com matrizes. Até lá.