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.

Nenhum comentário:

Postar um comentário