Vetores - OBI2017olimpiada.ic.unicamp.br/extras/cursoC/Cap07-Vetores-texto.pdf · 24/3/2011 09:23 1 Vetores Nos capítulos anteriores estudamos as opções disponíveis na linguagem

  • Upload
    vobao

  • View
    215

  • Download
    0

Embed Size (px)

Citation preview

  • 24/3/2011 09:23 1

    Vetores Nos captulos anteriores estudamos as opes disponveis na linguagem C para representar:

    Nmeros inteiros em diversos intervalos.

    Nmeros fracionrios com vrias alternativas de preciso e magnitude.

    Letras, dgitos, smbolos e outros caracteres.

    Todas estas opes esto definidas previamente na prpria linguagem C e, portanto, so chamadas de tipos de dados da linguagem C.

    Agora, conheceremos os mecanismos para definir novos tipos de dados, conforme a necessidade do seu programa. Assim, eles so chamados de tipos de dados do programador.

    Vetores Um vetor uma seqncia de vrios valores do mesmo tipo, armazenados seqencialmente na memria, e fazendo uso de um mesmo nome de varivel para acessar esses valores. Um vetor tambm pode ser entendido logicamente como uma lista de elementos de um mesmo tipo.

    Cada elemento desta seqncia pode ser acessado individualmente atravs de um ndice dado por um nmero inteiro. Os elementos so indexados de 0 at n-1, onde n a quantidade de elementos do vetor. O valor de n tambm chamado de dimenso ou tamanho do vetor. O vetor tem tamanho fixo durante a execuo do programa, definido na declarao. Durante a execuo no possvel aumentar ou diminuir o tamanho do vetor. Note que a numerao comea em zero, e no em um. Essa uma fonte comum de erros.

    A Figura 1 ilustra um vetor com 10 elementos, denominados v0, v1, v9, todos eles de tipo int.

    importante saber que os elementos do vetor so armazenados sequencialmente na memria do computador. Assim, na figura, se cada valor de tipo int ocupar 4 bytes de memria, teremos 40 bytes consecutivos reservados na memria do computador para armazenar todos os valores do vetor. No entanto, por ora, no faremos uso explcito dessa informao, uma vez que o compilador se encarregar de enderear cada elemento do vetor automaticamente, conforme as necessiades do programador, como veremos.

    Declarao de vetores A declarao de vetores obedece mesma sintaxe da declarao de variveis. A diferena est no valor entre colchetes, que determina quantos elementos ele armazenar, ou seja, em outras palavras, determina o seu tamanho ou dimenso.

    int[10] v1 v2 v3 v4 v5 v6 v7 v8 v9v0int[10] v1 v2 v3 v4 v5 v6 v7 v8 v9v0 Figura 1 Exemplo de vetor com 10 elementos

  • 24/3/2011 09:23 2

    tipo varivel[tamanho];

    Por exemplo, para declarar um vetor com 10 nmeros inteiros: int vetor[10];

    O tamanho precisa ser necessariamente um nmero inteiro e constante. Ele no pode ser resultado de uma expresso:

    int tamanho = 10; int vetor[tamanho*2]; // ERRADO!

    Acesso ao contedo de vetores J aprendemos que uma invocao de nome de varivel capaz de acessar um dado na memria. O tipo do valor retornado igual ao tipo utilizado na declarao da varivel. O nome da varivel tambm chamado de uma expresso de referncia de memria, ou simplesmente de referncia de memria.

    Com os vetores, temos uma nova expresso de referncia de memria: o operador de ndice [ ]. Ele utiliza uma referncia de memria (normalmente uma varivel do tipo vetor) e um nmero inteiro (o ndice). Ele retorna uma referncia para o elemento correspondente ao ndice. O tipo do valor retornado o mesmo tipo da declarao do vetor.

    Por exemplo, para atribuir o valor 3 na primeira posio do vetor, escrevemos: vetor[0] = 3;

    Note que o ndice zero indica a primeira posio no vetor. A expresso vetor[0] referencia a posio de memria correspondente ao elemento de ndice zero no vetor. Para somar os primeiros trs elementos e armazenar o valor calculado no quarto elemento, escrevemos:

    vetor[3] = vetor[0] + vetor[1] + vetor[2];

    Em expresses, uma referncia indexada a um vetor pode ser usada da mesma forma e nas mesmas posies em que usaramos variveis convencionais de mesmo tipo. Tudo se passa como se tivssemos vrias variveis declaradas simultaneamente, todas de mesmo tipo, e com nomes vetor[0] , vetor[1] , e assim por diante.

    muito comum utilizar a estrutura de repetio for para percorrer todos os elementos de um vetor. Por exemplo, para imprimir todos os elementos de um vetor de 100 elementos:

    int indice; int vetor[100]; ... for (indice = 0; indice < 100; indice++) { printf("%d", vetor[indice]); }

    Lembre-se que para um vetor de tamanho 100, o primeiro elemento tem ndice 0 e o ltimo elemento tem ndice 99. Por este motivo, o for repete para valores de indice variando de 0 at 99. A condio utilizada para terminar o for indice < 100 . Isto sugere que o for est realizando 100 repeties, que justamente o tamanho do vetor. A condio indice

    ndice

    (int)

    [ ]

    (ref. memria)

    varivel

    (ref. memria)

    ndice

    (int)

    [ ]

    (ref. memria)

    varivel

    (ref. memria) Figura 2 Operador ndice

  • 24/3/2011 09:23 3

    = 0; indice--) { printf( "%d " , valores[indice]); } return 0; }

    Consulte: Vetores\Reverso01\Reverso01.vcproj

    Descrio passo a passo: int valores[ 10]; int indice;

    A primeira varivel, valores, um vetor de nmeros inteiros, com 10 elementos. Eles so numerados seqencialmente de 0 at 9. Este vetor armazenar os valores digitados pelo usurio. A segunda declarao cria uma varivel contadora para o for de leitura e o for de escrita de valores.

    for (indice = 0; indice < 10; indice++) { scanf( "%d" , &valores[indice] ); }

    A estrutura de repetio for executa 10 vezes, variando o valor de indice desde 0 at 9. A cada repetio, o comando scanf l um nmero inteiro e o armazena no indice -simo elemento do vetor valores . Note que tal como no scanf para variveis comuns, a referncia valores[indice] se comporta como o nome de uma varivel comum de tipo int e necessrio preced-la pelo smbolo &.

    for (indice = 9; indice >= 0; indice--) { printf( "%d " , valores[indice]); }

  • 24/3/2011 09:23 4

    O vetor contm os 10 nmeros lidos no for anterior. Agora, vamos usar novamente a estrutura de repetio for para imprimir o vetor de trs para frente. Por este motivo, fazemos o ndice variar de 9 para 0.

    Contedo inicial de vetores Na declarao, pode-se definir o valor inicial de cada elemento de um vetor. A sintaxe semelhante declarao de uma varivel comum com valor inicial, mas os elementos so listados entre as chaves { e } e separados por vrgula.

    tipo varivel[n] = { elem 0, elem 1, elem 2, elem 3, ... elem n-1 };

    Como o nmero de elementos do vetor pode ser inferido a partir da lista entre chaves, podemos omitir o tamanho do vetor:

    tipo varivel[] = { elem 0, elem 1, elem 2, elem 3, ... elem n-1 };

    Regras para acesso ao vetor O programador deve observar algumas restries na manipulao de variveis que representam vetores.

    ndices invlidos Os elementos so numerados sempre de 0 at tamanho-1. Caso o programa tente acessar erroneamente um elemento de ndice negativo ou de ndice alm do tamanho do vetor, as conseqncias podero ser imprevisveis. No melhor dos casos, o sistema operacional detectar essa anomalia e o programa ser finalizado sinalizando um erro de execuo.

    Atribuir o valor de todos os elementos de uma s vez

    Exceto na declarao do vetor, no possvel atribuir valores a todos os elementos em uma s linha. Cada elemento precisa ser acessado individualmente. Tampouco possvel usar um nico scanf para ler todo o contedo do vetor. O cdigo abaixo est, portanto, errado:

    int vetor[10]; // inicializar todos os elementos com valor 0 vetor = 0; // ERRADO!

    O correto utilizar uma estrutura de repetio for para atribuir o valor a cada elemento. int vetor[10]; int indice; // inicializar todos os elementos com o valor 0 for (indice = 0; indice < 10; indice++) { vetor[indice] = 0; }

    Copiar vetores

    Tampouco possvel copiar o contedo de um vetor para um outro, mesmo que os dois sejam de mesmo tamanho e os elementos sejam de mesmo tipo.

    int vetorA[10], vetorB[10];

  • 24/3/2011 09:23 5

    // copiar o contedo do vetor B para o vetor A vetorA = vetorB; // ERRADO!

    O correto utilizar uma estrutura de repetio for para copiar um elemento de cada vez. int vetorA[10], vetorB[10]; int indice; // copiar o contedo do vetor B para o vetor A for (indice = 0; indice < 10; indice++) { vetorA[indice] = vetorB[indice]; }

    Vetor de tamanho varivel Na linguagem C, todos os vetores tm um tamanho fixo, e que no pode variar durante a execuo do programa. Mas como proceder quando o tamanho do vetor no pode ser previsto at o momento da execuo?

    Considere um programa que l n valores e os armazena no vetor. O valor n informado pelo usurio durante a execuo do programa. A linguagem C no oferece recursos para se declarar um vetor cujo tamanho se ajuste automaticamente ao nmero n de elementos.

    A soluo mais simples declarar o vetor com o tamanho mximo necessrio para tratar o pior caso. O nmero de elementos realmente utilizado ficar armazenado em uma outra varivel. Uma abordagem mais flexvel ser apresentada mais tarde, utilizando alocao dinmica de memria, mas esta uma tcnica mais complexa.

    Continuando o exemplo, vamos supor que foi utilizado algum critrio para descobrir que o vetor nunca precisar armazenar mais do que 100 elementos.

    int valores[100]; int numero_elementos;

    Para imprimir todos os elementos do vetor, procedemos como antes, utilizando uma estrutura de repetio for. Mas, ao invs de utilizar o tamanho mximo como condio de parada, comparamos o ndice com o tamanho dado pela varivel numero_elementos .

    for (indice = 0; indice < numero_elementos; indice+ +) { printf("%d", vetor[indice]; }

    Apesar do nmero de celas do vetor que estamos usando ser varivel (o nmero dado pelo contedo da varivel numero_elementos ) a capacidade mxima do vetor foi declarada como 100 elementos. O programa precisa garantir que a quantidade de elementos utilizada nunca extrapole o nmero mximo de elementos declarados para o vetor. Caso contrrio, o resultado da execuo do programa ser imprevisvel! A no verificao do valor usado como ndice em um vetor uma das causas mais freqentes de falha de

    software e violao de segurana.

    Exemplo

    Programa que l n valores, armazena-os em um vetor e em seguida os imprime em ordem inversa.

    Cdigo fonte: #include #include int main( int argc, char *argv[]) { int valores[ 100 ];

  • 24/3/2011 09:23 6

    int numero_valores; int indice; printf( "Quantos valores? (no mximo 100): " ); scanf( "%d" , &numero_valores); if ( (numero_valores > 100) || (numero_valores < 0) ) { printf( "Nmero de valores invlido\n" ); return 1; } printf( "Escreva %d nmeros inteiros: " , numero_valores); for (indice = 0; indice < numero_valores; indice++) { scanf( "%d" , &valores[indice] ); } printf( "Valores em ordem reversa:\n" ); for (indice = numero_valores- 1; indice >= 0; indice--) { printf( "%d " , valores[indice]); } return 0; }

    Consulte: Vetor\Reverso02\Reverso02.vcproj

    Descrio passo a passo: int valores[ 100];

    A varivel valores declarada como um vetor de 100 elementos de tipo inteiro. Ele armazenar os valores digitados pelo usurio.

    int numero_valores;

    A varivel numero_valores armazena quantos elementos do vetor valores esto realmente sendo utilizados para armazenar os valores digitados pelo usurio.

    int indice;

    A declarao cria uma varivel contadora para o for de leitura e o for de escrita de valores.

    printf( "Quantos valores? (no mximo 100): " ); scanf( "%d" , &numero_valores);

    Pede ao usurio para informar de quantos elementos consiste a lista de nmeros. Esse valor ser armazenado na varivel numero_valores e passar a controlar o nmero de repeties do for.

    if ( (numero_valores > 100) || (numero_valores < 0) ) { printf( "Nmero de valores invlido!\n" ); return 1; }

    Verifica se o nmero de elementos que o usurio digitou est dentro do limite suportado pelo programa. Esta verificao essencial para garantir que nenhuma operao de indexao do vetor seja realizada com ndice maior que a capacidade do vetor, ou com ndice negativo, o que poderia comprometer a integridade do programa.

    for (indice = 0; indice < numero_valores; indice++) { scanf( "%d" , &valores[indice] ); }

  • 24/3/2011 09:23 7

    A estrutura de repetio for executa numero_valores vezes, variando o contedo de indice de 0 at numero_valores-1 . A cada repetio, o comando scanf l um nmero inteiro e o armazena no indice -simo elemento do vetor valores . Note que tal como no scanf para variveis comuns, o nome do vetor precedido pelo smbolo &.

    for (indice = numero_valores- 1; indice >= 0; indice--) { printf( "%d " , valores[indice]); }

    O vetor contm numero_valores nmeros lidos no for anterior. Agora, vamos usar novamente a estrutura de repetio for para imprimir o vetor de trs para frente. Por este motivo, fazemos o ndice variar de numero_valores-1 para 0.

    Matrizes e vetores multidimensionais A noao de matriz a generalizao imediata da noo de vetor. Uma matriz uma tabela de vrios valores do mesmo tipo, armazenados seqencialmente e fazendo uso de um mesmo nome de varivel para acessar esses valores.

    Cada elemento da tabela pode ser acessado individualmente atravs de dois ndices com valores inteiros. Estes ndices poderiam ser interpretados como a linha e a coluna da matriz.

    A linguagem C define uma matriz como um vetor, cujos elementos so novamente vetores de mesmo tamanho e tipo. Na verdade, o nmero de linhas corresponde ao nmero de elementos do vetor externo, e o nmero de colunas o tamanho dos vetores internos que constituem cada elemento dos vetores externos. A Figura 3 ilustra este conceito:

    Todos os conceitos e todas as regras vlidas para os vetores tambm so vlidas para as matrizes. A matriz tem tamanho fixo, definido na declarao. As linhas so numeradas de 0 at linhas-1 e as colunas de 0 at colunas-1.

    Declarao de matrizes A declarao de matrizes obedece a mesma sintaxe que a declarao de vetores, exceto pela adio de uma nova dimenso escrita entre colchetes[ ] .

    tipo varivel[linhas][colunas];

    Por exemplo, para declarar uma matriz de nmeros inteiros com 4 linhas e 10 colunas: int matriz[4][10];

    int[10] a01 a02 a03 a04 a05 a06 a07 a08 a09a00

    int[10] a11 a12 a13 a14 a15 a16 a17 a18 a19a10

    int[10] a21 a22 a23 a24 a25 a26 a27 a28 a29a20

    int[10] a31 a32 a33 a34 a35 a36 a37 a38 a39a30

    int[4][10]

    int[10] a01 a02 a03 a04 a05 a06 a07 a08 a09a00int[10] a01 a02 a03 a04 a05 a06 a07 a08 a09a00

    int[10] a11 a12 a13 a14 a15 a16 a17 a18 a19a10int[10] a11 a12 a13 a14 a15 a16 a17 a18 a19a10

    int[10] a21 a22 a23 a24 a25 a26 a27 a28 a29a20int[10] a21 a22 a23 a24 a25 a26 a27 a28 a29a20

    int[10] a31 a32 a33 a34 a35 a36 a37 a38 a39a30int[10] a31 a32 a33 a34 a35 a36 a37 a38 a39a30

    int[4][10]

    Figura 3 Exemplo de uma matriz com 4 linhas e 10 colunas

  • 24/3/2011 09:23 8

    A atribuio ou leitura de valores em uma matriz utiliza dois ndices. O primeiro elemento armazenado em matriz[0][0] o segundo em matriz[0][1] e assim por diante, at o ltimo elemento, que armazenado em matriz[linhas-1][colunas-1].

    Para atribuir o valor 3 na linha 2, coluna 5, escrevemos: matriz[1][4] = 3;

    Note como a linha 2 numerada como 1, pois comeamos a contar a partir do zero. Idem, a coluna 5 numerada como 4.

    Para percorrer os elementos de uma matriz, so necessrias duas estruturas de repetio for, uma dentro da outra. O for externo percorre as linhas da matriz, o for interno percorre as colunas de uma determinada linha que est fixada pelo for externo. Por exemplo, para imprimir todos os elementos de uma matriz 4x10, linha por linha:

    int linha, coluna; int matriz[4][10]; ... for (linha = 0; linha < 4; linha++) { for (coluna = 0; coluna < 10; coluna++) { printf("%d ", matriz[linha][coluna]); } printf("\n"); }

    para cada valor de linha fixado pelo for externo entre 0 e 3, o for interno executa, varrendo as colunas correspondentes quela linha, de 0 a 9. Assim, quando o for interno excecuta, o valor de linha est fixado pelo for externo, fazendo com que o printf imprima os valores da linha linha da matriz. Antes de retornar ao for externo imprimimos um caracter para mandar o cursor para a prxima linha.

    Declarao de vetores multidimensionais Para cada nova dimenso que desejarmos adicionar ao vetor, devemos adicionar um novo tamanho entre colchetes [ ] . Para um vetor com n dimenses:

    tipo varivel[tamanho 1][tamanho 2]...[tamanho N];

    Textos ou Cadeias (strings) J utilizamos seqncias de caracteres (que tambm chamaremos de textos) diversas vezes, em conjunto com os comandos printf e scanf :

    printf("Digite dois nmeros: "); scanf("%d %d", &a, &b);

    Neste caso, "Digite dois nmeros: " e "%d %d" so dois exemplos de textos em C.

    Armazenamento de seqncias de texto

    Em captulo anterior aprendemos como utilizar o tipo char para representar uma nica letra, um nico dgito ou um nico smbolo. De fato, o tipo char armazena o ndice da tabela ASCII correspondente ao caractere. Mas ele por si s no capaz de armazenar um texto formado por vrios caracteres.

    Para obter esse efeito na linguagem C, armazenamos o texto como um vetor de caracteres, onde cada caractere do texto um elemento do vetor. O fim do texto deve ser demarcado

  • 24/3/2011 09:23 9

    por um caractere adicional e especial: o caractere nulo, que simplesmente um caractere cujo valor numrico zero. Ele representado como '\0' . Por este motivo, o comprimento do vetor de caracteres sempre no mnimo o nmero de caracteres no texto mais 1. Como veremos, em alguns casos, o caractere de valor zero adicionado automaticamente ao texto quando este manipulado diretamente na linguagem C.

    Por ser um vetor de caracteres, cujo primeiro elemento tem ncice 0, o acesso a um determinado ndice i retornar o caractere que est na posio i+1 do texto.

    A Figura 4 ilustra como um programa escrito em C armazenaria a seqncia Um texto em C, que tem 13 smbolos, em um vetor de caracteres de tamanho 20. Como o vetor indexado a partir do nmero 0, os caracteres que formam texto ficaro guardados do ndice 0 at o ndice 12. Logo em seguida, no ndice 13, armazenado o caractere \0 . Ele foi adicionado automaticamente e indica que o texto terminou na posio anterior. Esta marcao importante, pois o comprimento do texto menor que o tamanho mximo disponvel no vetor. No espao restante, do ndice 14 at 19, o vetor contm caracteres indeterminados. Para saber a quinta letra do texto, consulta-se texto[4] , que retornar o caractere e .

    Diferentemente de linguagens de programao de mais alto nvel, a linguagem C oferece um conjunto mais limitado de recursos para manipular textos diretamente. No entanto, o arquivo de incluso string.h contm vrias rotinas pr-compiladas para se lidar com cadeias de caracteres em C.

    Declarao de variveis de tipo texto Uma varivel para armazenar textos declarada como um vetor de caracteres:

    char texto[tamanho];

    Note que tamanho o comprimento do vetor, ou seja, ele pode armazenar um texto com no mximo tamanho-1 caracteres (pois a ltima posio conter sempre o caractere '\0' ). Quando no sabemos de antemo o comprimento do texto que desejamos armazenar, ser necessrio utilizar um limitante superior de pior caso para o tamanho. Esta abordagem foi discutida para vetores na seo anterior.

    Para declarar uma varivel que pode armazenar at 100 caracteres, devemos declarar um vetor de tamanho 101 (uma posio a mais para o caractere '\0' ):

    char texto[101];

    O texto armazenado pela varivel pode ser atribudo logo na declarao:

    char texto[tamanho] = "texto";

    Novamente, o tamanho deve ser pelo menos uma unidade maior que o comprimento de "texto" , para poder abrigar o caractere '\0' no final da seqncia, que adicionado automaticamente pelo compilador C. Note como o texto uma seqncia de caracteres colocados entre aspas duplas.

    char[20] m t eU t o ex C \0m0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

    char[20] m t eU t o ex C \0m0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

    Figura 4 Seqncia de caracteres de tamanho 20

  • 24/3/2011 09:23 10

    Para declarar uma varivel com o texto da Figura 4, declaramos: char texto[20] = "Um texto em C";

    Quando uma varivel deve armazenar um texto que no ser alterado no decorrer do programa, podemos omitir o tamanho na declarao. O compilador determinar automaticamente o tamanho do vetor de caracteres.

    char texto[] = "texto";

    Escrita

    Para imprimir o contedo de um vetor de caracteres utilizamos o comando printf , com o formatador %s:

    char texto[] = "Um texto em C"; printf("A variavel texto contem: %s", texto);

    Que imprimir: A variavel texto contem: Um texto em C

    Leitura

    O comando scanf , com o modificador %s, capaz de ler uma seqncia de caracteres, terminada com fim de linha ou espao em branco.

    char texto[20]; printf("Escreva uma palavra: "); scanf("%s", texto);

    Observaes:

    Note que neste caso particular de scanf no h o smbolo & em frente do nome da varivel! Isto porque a varivel, na verdade, representa um vetor, e no uma varivel simples.

    A varivel, do tipo vetor de caracteres, precisa de espao suficiente para receber o texto digitado pelo usurio. Se isso no for verdade, o resultado ser imprevisvel.

    O comando scanf adiciona automaticamente o caractere especial '\0' aps o texto lido.

    A leitura terminar aps a primeira seqncia de caracteres no nulos (aqueles diferentes de nova linha e de tabulao) que for encontrada.

    Para ler uma linha completa podemos utilizar gets: char texto[20]; gets(texto);

    O comando gets l todos os caracteres at que o usurio pressione ENTER. O caractere '\0' adicionado no fim do texto. Note que gets est no aquivo string.h , o qual deve ser includo. Neste caso, portanto, se o usurio digitar mais de 19 caracteres teremos problemas.

    Atribuio Por designarem vetores de caracteres, no se pode usar o operador de atribuio com variveis de tipo texto:

    char texto[20] = "Um texto em C"; texto = "Outro texto em C"; //ERRADO!

  • 24/3/2011 09:23 11

    Para esta operao, a linguagem C oferece a funo strcpy , definida na biblioteca string.h :

    #include ... char texto[20] = "Um texto em C"; strcpy(texto, "Outro texto em C");

    O vetor de caracteres destino o primeiro argumento da funo strcpy , o texto a ser copiado est no segundo argumento. A funo strcpy automaticamente adicionar o caractere '\0' ao primeiro vetor, para indicar a posio do fim do texto. O vetor destino precisa ter tamanho suficiente para receber o novo texto mais o caractere '\0' . H vrias outras funes que manipulam cadeias em C. Consulte a documentao do seu compilador.

    Se desejarmos modificar apenas um caractere, podemos utilizar seu ndice, aproveitando o fato de C representar textos como vetores de caracteres:

    char texto[20] = "Um texto em C"; texto[12] = 'B'; printf("%s", texto);

    Imprimir: Um texto em B