Baixe o app para aproveitar ainda mais
Prévia do material em texto
Programação de Computadores II Prof. Erlon Pinheiro 2 Programação de Computadores II Prof. Erlon Pinheiro Sumário Unidade 1: MODULARIZAÇÃO ..................................................................................... 5 1.1 - Introdução .............................................................................................................. 5 1.2 - MODULARIZAÇÃO EM C ................................................................................. 6 1.2.1 - Introdução às Funções .................................................................................... 6 1.2.2 – Argumentos e Passagem de parâmetros ........................................................ 8 1.2.3 - Retornando valores ......................................................................................... 9 1.2.4 – Passagem de parâmetros por valor ............................................................. 11 1.2.5 – PONTEIROS ............................................................................................... 12 1.2.5.2 - Declarando e Utilizando Ponteiros ............................................................ 12 1.2.6 - Passagem de parâmetros por valor e passagem por referência .................... 15 1.2.7 - Variáveis globais .......................................................................................... 19 1.2.8 - Ponteiros e Vetores ....................................................................................... 19 Exercícios Complementares 1 .................................................................................... 23 Unidade 2: Strings ........................................................................................................... 29 2.1 - Introdução ............................................................................................................ 29 2.2 – Leitura de Strings ................................................................................................ 30 2.3 – Criando funções que manipulam strings ............................................................ 32 2.4 – Funções pré-definidas para manipulação de strings ........................................... 34 a) gets ....................................................................................................................... 34 c) strcpy ................................................................................................................... 35 d) strcat .................................................................................................................... 35 e) strlen .................................................................................................................... 36 f) strcmp ................................................................................................................... 36 g) toupper ................................................................................................................. 37 Exercícios Complementares 2 ..................................................................................... 37 Unidade 3: Matrizes ......................................................................................................... 40 3.1 – Introdução: Definições Iniciais ........................................................................... 40 3.2 - Matrizes de strings .............................................................................................. 40 3.3 - Matrizes multidimensionais ................................................................................ 41 3.4 - Inicialização ........................................................................................................ 41 3.4.1 - Inicialização sem especificação de tamanho ................................................ 42 3.5 – Passagem de Matrizes para Funções .................................................................. 43 3.6 – Tipos Básicos de Matrizes .................................................................................. 43 Exercícios Complementares 3 ..................................................................................... 45 Unidade 4: Pesquisa e Ordenação de Vetores ................................................................. 49 4.1 - Ordenação em vetores ......................................................................................... 49 4.2 - Pesquisa em vetores ............................................................................................ 52 4.2.1 - Método Pesquisa Sequencial........................................................................ 52 4.2.2 - Pesquisa Binária .......................................................................................... 54 Exercícios de Complementares 4 ................................................................................ 57 Unidade 5: Estruturas ...................................................................................................... 58 5.1 – Introdução ao Uso de Structs .............................................................................. 58 5.2 – Definição de Tipos ............................................................................................. 58 5.3 – Declarando variáveis do tipo estrutura ............................................................... 58 5.4 - Vetores de estruturas ........................................................................................... 61 5.5 - Atribuição entre estruturas do mesmo tipo ......................................................... 62 5.6 - Passando uma estrutura para funções .................................................................. 63 5.7 – Ponteiros e Estruturas ......................................................................................... 64 Exercícios Complementares 5 ..................................................................................... 66 3 Programação de Computadores II Prof. Erlon Pinheiro (Dica: Estudar a seção 5.5 - Atribuição entre estruturas do mesmo tipo) ................... 69 Unidade 6: Arquivos ........................................................................................................ 71 6.1 – Introdução: Definições e uso de Arquivos ......................................................... 71 6.2 - Abrindo e Fechando um Arquivo .................................................................... 71 - fopen ...................................................................................................................... 71 - fclose ..................................................................................................................... 72 6.3 – Finalizando a execução de um programa .......................................................... 72 Comando exit ........................................................................................................... 72 6.4 - Lendo e Escrevendo Caracteres em Arquivos ..................................................... 73 Função putc .............................................................................................................. 73 Função getc .............................................................................................................. 74 6.5 - Outros Comandos de Acesso a Arquivos ............................................................ 76 Função fgets ............................................................................................................ 76 Função fputs ............................................................................................................ 76 Fluxos Padrão .......................................................................................................... 76 Função fprintf ......................................................................................................... 77 Função fscanf ........................................................................................................... 77 Exercícios Complementares 6 ..................................................................................... 78 Referências Bibliográficas ............................................................................................... 80 4 Programação de Computadores II Prof. Erlon Pinheiro Unidade 1: MODULARIZAÇÃO 1.1 - Introdução Estudos da psicologia mostram que o ser humano normal é capaz de resolver problemas que envolvam 7 (com incerteza de 2) variáveis simultaneamente. Um problema é complexo se envolve mais do que 9 ou 10 variáveis. Como, então, podemos resolver problemas desse tipo? A solução que encontramos para solucionar esses problemas é dividir um problema complexo em vários subproblemas elementares, onde não estejamos sujeitos a esta limitação humana. Esta estratégia de solução de problemas é comumente conhecida como a técnica de Dividir para Conquistar, a qual é o fundamento básico para a Modularização. Neste sentido a modularização de programas consiste da divisão do programa em módulos ou subprogramas, que nada mais são do que um conjunto de comandos agrupados com a finalidade de executar uma determinada função. Além disso, a modularização é uma técnica de programação que permite a aplicação direta dos refinamentos sucessivos, isto é, possibilita que haja um mapeamento entre os diversos tipos de algoritmos criados quando utilizamos refinamentos e o próprio ato de programar. Outro problema que nos defrontamos quando estamos programando é a repetição de código. Você já deve ter notado que certos trechos de seus programas se repetem em vários momentos, igualmente como foram escritos num primeiro instante ou de uma maneira muito aproximada. Esta repetição decódigo, além de ser algo desagradável para o programador, torna o programa redundante e dificulta, em última instância, a sua legibilidade. A modularização é uma solução natural para este problema, pois um módulo pode ser ativado em pontos diferentes de um mesmo programa. Para falarmos um pouco mais sobre as vantagens da modularização necessitamos definir alguns conceitos sobre qualidade de programas. Um programa é de boa qualidade quando apresenta as seguintes características: • Confiabilidade: o programa está correto e é confiável. • Legibilidade: o programa é fácil de ser lido por um programador de nível médio. • Manutenibilidade: o programa é fácil de ser modificado. Quando o usuário requer modificações no programa, fruto de novas necessidades, a atualização do programa deve ser natural. • Flexibilidade: o programa é fácil de ser reutilizado; não possui muitas restrições. Nesse contexto, podemos citar agora uma série de vantagens da modularização: Facilita o projeto de sistemas: Incentiva a aplicação da técnica de refinamentos sucessivos. Minimiza a manutenção de programas: 5 Programação de Computadores II Prof. Erlon Pinheiro Modificações no programa precisam ser realizadas apenas no módulo correspondente, sem ter de alterar o resto do programa. Melhora a legibilidade dos programas: Não há necessidade de se entrar em detalhes de programação para se entender o que o programa faz. Viabiliza a validação: Testes e correções de cada segmento podem ser realizados em separado, só reunindo os módulos quando todos estiverem devidamente validados. Permite a divisão de tarefas em uma equipe: Cada módulo pode ser realizado por um programador diferente. Facilita a reutilização de código: Em trechos onde ocorre repetição de código no programa Em trechos onde ocorre repetição parcial de código, onde só são modificados os argumentos utilizados. Estimula o encapsulamento de informações: O programador que usará o módulo só precisa saber o que o módulo faz e não como faz 1.2 - MODULARIZAÇÃO EM C Agora que já estamos familiarizados com o conceito de modularização e conhecemos as suas vantagens, podemos nos ater a técnica de modularização que é implementada em C. Em C para modularizar programas usaremos funções. 1.2.1 - Introdução às Funções Na linguagem C tudo gira em torno das funções. Por exemplo, a função de nome main é obrigatória em todos os programas em C, pois é esta função que será chamada quando o programa for executado. O conteúdo da função é delimitado por chaves { }. O código que estiver dentro das chaves será executado seqüencialmente quando a função for chamada. Exemplos de outras funções que já conhecemos: a função printf () e a função scanf (). Uma função é um bloco de código de programa que pode ser usado diversas vezes em sua execução. O uso de funções permite que o programa fique mais legível, mais bem estruturado. Um programa em C consiste, no fundo, de várias funções colocadas juntas. Abaixo o tipo mais simples de função: 6 Programação de Computadores II Prof. Erlon Pinheiro #include <stdio.h> #include <stdlib.h> void mensagem (void) // Função simples: só imprime Ola! { printf ("Ola! "); } main () { mensagem(); printf ("Eu estou estudando C!\n"); system(“pause”); } Este programa define uma função mensagem() que imprime apenas a mensagem Ola! na tela. O primeiro void na especificação da função mensagem() significa que a função não retornará valor algum e o void entre parênteses significa que a função mensagem não recebe valor algum para sua execução. Estudaremos mais a frente maiores detalhes sobre utilização de argumentos. A função mensagem() é chamada a partir de main() , que, como já vimos, também é uma função. A diferença fundamental entre main e as demais funções do programa é que main é a primeira função a ser executada pelo programa. Obs.: A função pode ser implementada depois da função main(), mas neste caso necessitamos colocar no início do programa o protótipo da função, por exemplo, o código anterior ficaria assim: #include <stdio.h> #include <stdlib.h> void mensagem (void); main() { mensagem(); printf ("Eu estou estudando C!\n"); system(“pause”); } void mensagem (void) // Função simples: só imprime Ola! { printf ("Ola! "); } Exercício de Fixação 7 Programação de Computadores II Prof. Erlon Pinheiro 1) Faça um programa que utilize uma função para mostrar na tela a soma de dois números lidos dentro da função. 2) Faça um programa que utilize uma função para calcular e mostrar na tela o fatorial de um número. 1.2.2 – Argumentos e Passagem de parâmetros Parâmetros são as entradas que a função recebe no instante da ativação do módulo. É através dos parâmetros que podemos nos “comunicar” com a função. Já utilizamos funções com parâmetros. As funções printf() e scanf() são funções que recebem uma expressão de controle e uma lista de argumentos. Obs.: Costumamos dizer que na hora da chamada passamos o argumento que será um parâmetro na execução da função. Vamos ver outro exemplo simples de função com passagem de parâmetros: #include <stdio.h> #include <stdlib.h> void quadrado(int x) /* Calcula o quadrado de x */ { printf ("O quadrado e %d.\n",(x*x)); } main () { int num; printf ("Entre com um numero: "); scanf ("%d",&num); printf ("\n\n"); quadrado(num); system("pause"); } 8 Programação de Computadores II Prof. Erlon Pinheiro Na definição de quadrado() dizemos que a função receberá um parâmetro inteiro x. Quando fazemos a chamada à função, o inteiro num é passado como argumento. Há alguns pontos a observar. Em primeiro lugar temos de satisfazer aos requisitos da função quanto ao tipo e à quantidade de argumentos quando a chamamos. Apesar de existirem algumas conversões de tipo, que o C faz automaticamente, é importante ficar atento. Em segundo lugar, não é importante o nome da variável que se passa como argumento, ou seja, a variável num, ao ser passada como argumento para quadrado() é copiada para a variável x. Dentro de quadrado() trabalha-se apenas com x. Se mudarmos o valor de x dentro de quadrado() o valor de num na função main() permanece inalterado. O próximo exemplo possui uma função que recebe como parâmetros mais de uma variável. Repare que, neste caso, os argumentos são separados por vírgula e devemos explicitar o tipo de cada um dos parâmetros, um a um. Note também que os argumentos passados para a função não necessitam ser todos variáveis, porque mesmo sendo constantes serão copiados para a variável de entrada da função. #include <stdio.h> #include <stdlib.h> void mult(float a, float b, float c)// Multiplica 3 números { printf ("%.1f * %.1f * %.1f = %.1f.\n\n",a,b,c,a*b*c); } main () { float x,y; x=23.5; y=12.9; mult (x,y,3.87); system("pause"); } Exercício de Fixação 1) Faça um programa que utilize uma função para mostrar na tela a soma de dois números lidos fora da função. 2) Faça um programa que utilize uma função para calcular e mostrar na tela o fatorial de um número. O número deve ser lido na função main() e passado para a função calcular o fatorial. 1.2.3 - Retornando valores Muitas vezes é necessário fazer com que uma função retorne um valor. As funções que vimos até aqui não retornavam valores (usavam o void). Podemos especificar um tipo de retorno indicando-o antes do nome da função (no lugar do void). Mas para dizer ao C o que vamos 9 Programação de Computadores II Prof. Erlon Pinheiro retornar,precisamos da palavra reservada return. Sabendo disto fica fácil fazer uma função para multiplicar dois inteiros e que retorna o resultado da multiplicação. Veja: #include <stdio.h> #include <stdlib.h> int prod (int x,int y) { return (x*y); } main () { int saida; saida=prod (12,7); printf ("A saida e: %d\n",saida); system(“pause”); } Veja que, como prod retorna o valor de 12 multiplicado por 7, este valor pode ser usado em uma expressão qualquer. No programa fizemos a atribuição deste resultado à variável saida, que posteriormente foi impressa usando o printf. Uma observação adicional: se não especificarmos o tipo de retorno de uma função, o compilador C automaticamente suporá que este tipo é inteiro. Porém, não é uma boa prática não se especificar o valor de retorno e, neste curso, este valor será sempre especificado. Com relação à função main, o retorno sempre será inteiro. Podemos fazer a função main retornar um zero quando ela é executada sem qualquer tipo de erro. Mais um exemplo de função, que agora recebe dois floats e também retorna um float: #include <stdio.h> #include <stdlib.h> float prod (float x,float y) { return (x*y); } int main (void) { float saida; saida=prod (45.2,0.0067); printf ("A saida e: %8.5f. \n",saida); system("pause"); } - Forma geral Apresentamos aqui a forma geral de uma função: 10 Programação de Computadores II Prof. Erlon Pinheiro tipo_de_retorno nome_da_função (lista_de_parâmetros) { código_da_função } Exercícios de Fixação 1) Faça um programa que utilize uma função para calcular a soma de dois números lidos fora da função. O cálculo deve ser retornado para ser impresso pela função main(). 2) Faça um programa que utilize uma função para calcular o fatorial de um número. O número deve ser lido na função main(), passado para a função calcular o fatorial e o resultado deve ser impresso na função main(). 1.2.4 – Passagem de parâmetros por valor Até o momento utilizamos passagem de parâmetros por valor. Isto significa que qualquer alteração feita em um parâmetro dentro de uma função não alterou o seu argumento (fora da função), por exemplo: #include <stdio.h> #include <stdlib.h> void mostra_valor(int a); main() { int a; a = 0; mostra_valor(a); printf("Valor de a fora da função: %d.\n\n",a); system("pause"); } void mostra_valor (int a) { a = 1; printf("Valor de a dentro da função: %d.\n\n",a); } Existe outro tipo de passagem de parâmetros que iremos estudar posteriormente: passagem de parâmetros por referência. Este tipo de passagem de parâmetros altera o valor do argumento passado para uma função. Mas para usarmos este tipo de passagem precisamos entender o que são variáveis do tipo ponteiro. 11 Programação de Computadores II Prof. Erlon Pinheiro 1.2.5 – PONTEIROS O C é altamente dependente dos ponteiros. Assim, para um programador em C é fundamental dominar o básico (pelo menos) deles. Por isto, recomendo ao leitor um carinho especial com esta parte do curso que trata deles. Ponteiros são tão importantes na linguagem C que você já os viu e nem percebeu, pois mesmo para se fazer uma introdução básica à linguagem C precisa-se deles. O Ministério da Saúde adverte: o uso descuidado de ponteiros pode levar a sérios bugs e a dores de cabeça terríveis :-). 1.2.5.1 - Como Funcionam os Ponteiros Os int s guardam inteiros. Os float s guardam números de ponto flutuante. Os char s guardam caracteres. Ponteiros guardam endereços de memória. Quando você anota o endereço de um colega você está criando um ponteiro. O ponteiro é este seu pedaço de papel. Ele tem anotado um endereço. Qual é o sentido disto? Simples. Quando você anota o endereço de um colega, depois você vai usar este endereço para achá-lo. O C funciona assim. Você anota o endereço de algo numa variável ponteiro para depois usar. Da mesma maneira, uma agenda, onde são guardados endereços de vários amigos, poderia ser vista como sendo uma matriz de ponteiros no C. Um ponteiro também tem tipo. Veja: quando você anota um endereço de um amigo você o trata diferente de quando você anota o endereço de uma firma. Apesar de o endereço dos dois locais ter o mesmo formato (rua, número, bairro, cidade, etc.) eles indicam locais cujos conteúdos são diferentes. Então os dois endereços são ponteiros de tipos diferentes. No C quando declaramos ponteiros nós informamos ao compilador para que tipo de variável vamos apontá-lo. Um ponteiro int aponta para um inteiro, isto é, guarda o endereço de um inteiro. 1.2.5.2 - Declarando e Utilizando Ponteiros Para declarar um ponteiro temos a seguinte forma geral: tipo_do_ponteiro *nome_da_variável; É o asterisco (*) que faz o compilador saber que aquela variável não vai guardar um valor, mas sim um endereço para aquele tipo especificado. Vamos ver exemplos de declarações: int *pt; char *temp,*pt2; O primeiro exemplo declara um ponteiro para um inteiro. O segundo declara dois ponteiros para caracteres. Eles ainda não foram inicializados (como toda variável do C que é apenas declarada). Isto significa que eles apontam para um lugar indefinido. Este lugar pode estar, por exemplo, na porção da memória reservada ao sistema operacional do computador. Usar o ponteiro nestas circunstâncias pode levar a um travamento do micro, ou a algo pior. 12 Programação de Computadores II Prof. Erlon Pinheiro O ponteiro deve ser inicializado (apontado para algum lugar conhecido) antes de ser usado! Isto é de suma importância! Para atribuir um valor a um ponteiro recém-criado poderíamos igualá-lo a um valor de memória. Mas, como saber a posição na memória de uma variável do nosso programa? Seria muito difícil saber o endereço de cada variável que usamos, mesmo porque estes endereços são determinados pelo compilador na hora da compilação e realocados na execução. Podemos então deixar que o compilador faça este trabalho por nós. Para saber o endereço de uma variável basta usar o operador &. Veja o exemplo: int cont=10; int *pt; pt=&cont; Criamos um inteiro cont com o valor 10 e um apontador para um inteiro pt. A expressão &cont nos dá o endereço de cont, o qual armazenamos em pt. Simples, não é? Repare que não alteramos o valor de cont, que continua valendo 10. Como nós colocamos um endereço em pt, ele está agora "liberado" para ser usado. Podemos, por exemplo, alterar o valor de cont usando pt. Para tanto vamos usar o operador "inverso" do operador &. É o operador *. No exemplo acima, uma vez que fizemos pt=&cont a expressão *pt é equivalente ao próprio cont. Isto significa que, se quisermos mudar o valor de cont para 12, basta fazer *pt=12. Vamos fazer uma pausa e voltar à nossa analogia para ver o que está acontecendo. Digamos que exista uma firma. Ela é como uma variável que já foi declarada. Você tem um papel em branco onde vai anotar o endereço da firma. O papel é um ponteiro do tipo firma. Você então liga para a firma e pede o seu endereço, o qual você vai anotar no papel. Isto é equivalente, no C, a associar o papel à firma com o operador &. Ou seja, o operador & aplicado à firma é equivalente a você ligar para a mesma e pedir o endereço. Uma vez de posse do endereço no papel você poderia, por exemplo, fazer uma visita à firma. No C você faz uma visita à firma aplicando o operador * ao papel. Uma vez dentro da firma você pode copiar seu conteúdo ou modificá-lo. Uma observação importante: apesar do símbolo ser o mesmo, o operador * (multiplicação)não é o mesmo operador que o * (referência de ponteiros). Para começar o primeiro é binário, e o segundo é unário pré-fixado. Aqui vão dois exemplos de usos simples de ponteiros: #include <stdio.h> #include <stdlib.h> main () { int num,valor; int *p; num=55; p=# /* Pega o endereço de num */ valor=*p; /* Valor e igualado a num de uma 13 Programação de Computadores II Prof. Erlon Pinheiro maneira indireta */ printf ("\n\n Variavel valor = %d \n",valor); printf ("Endereco para onde o ponteiro aponta: %p\n",p); printf ("Valor da variavel apontada: %d\n",*p); system("pause"); } #include <stdio.h> #include <stdlib.h> main () { int num,*p; num=55; p=# /* Pega o endereco de num */ printf ("\nValor inicial: %d\n",num); *p=100; /* Muda o valor de num de uma maneira indireta */ printf ("\nValor final: %d\n",num); system(“pause”); } Nos exemplos acima vemos um primeiro exemplo do funcionamento dos ponteiros. No primeiro exemplo, o código %p usado na função printf() indica à função que ela deve imprimir um endereço. Exercícios de Fixação Veja como você está. a) Explique o que é uma variável do tipo ponteiro. b) Qual o valor de x e y no final do programa? Tente primeiro descobrir e depois verifique no computador o resultado. A seguir, escreva um /* comentário */ em cada comando de atribuição explicando o que ele faz e o valor da variável à esquerda do '=' após sua execução. main() { int x, y, *p,*q; y = 1; x = 2; p = &y; q = &x; x--; //x = x -1; *p *= x+1;// *p = (*p)*(x+1); *q += y; // *q = *q +y; p = q; *p -= x; // *q = *q – x; printf ("y = %d\n", y); printf ("x = %d\n", x); 14 Programação de Computadores II Prof. Erlon Pinheiro } 1.2.6 - Passagem de parâmetros por valor e passagem por referência Já vimos que, na linguagem C, quando chamamos uma função os parâmetros formais da função copiam os valores dos parâmetros que são passados para a função. Isto quer dizer que não são alterados os valores que os parâmetros têm fora da função. Este tipo de chamada de função é denominado chamada por valor. Isto ocorre porque são passados para a função apenas os valores dos parâmetros e não os próprios parâmetros. Veja o exemplo abaixo: #include <stdio.h> #include <stdlib.h> float quadrado (float num); main () { float num,quad; printf ("Entre com um numero: "); scanf ("%f",&num); quad = quadrado(num); printf ("\n\nO numero original e: %f\n",num); printf ("O seu quadrado vale: %f\n",quad); system("pause"); } float quadrado (float num) { num=num*num; return num; } No exemplo acima o parâmetro formal num da função quadrado() sofre alterações dentro da função, mas a variável num da função main() permanece inalterada: é uma chamada por valor. Outro tipo de passagem de parâmetros para uma função ocorre quando alterações nos parâmetros formais, dentro da função, alteram os valores dos parâmetros que foram passados para a função. Este tipo de chamada de função tem o nome de "chamada por referência". Este nome vem do fato de que, neste tipo de chamada, não se passa para a função os valores das variáveis, mas sim suas referências (a função usa as referências para alterar os valores das variáveis fora da função). O C só faz chamadas por valor. Isto é bom quando queremos usar os parâmetros formais à vontade dentro da função, sem termos que nos preocupar em estar alterando os valores dos parâmetros que foram passados para a função. Mas isto também pode ser ruim às vezes, porque podemos querer mudar os valores dos parâmetros fora da função também. Há entretanto, no C, um recurso de programação que podemos usar para simular uma chamada por referência. Quando queremos alterar as variáveis que são passadas para uma função, nós podemos declarar seus parâmetros formais como sendo ponteiros. Os ponteiros são a "referência" que precisamos 15 Programação de Computadores II Prof. Erlon Pinheiro para poder alterar a variável fora da função. O único inconveniente é que, quando usarmos a função, teremos de lembrar de colocar um & na frente das variáveis que estivermos passando para a função. Veja um exemplo: #include <stdio.h> #include <stdlib.h> void troca (int *a,int *b); main () { int num1,num2; num1=100; num2=200; troca (&num1,&num2); printf ("\n\nEles agora valem %d %d\n",num1,num2); system("pause"); } void troca (int *a,int *b) { int temp; temp=*a; *a=*b; *b=temp; } Não é muito difícil. O que está acontecendo é que passamos para a função troca() o endereço das variáveis num1 e num2. Estes endereços são copiados nos ponteiros a e b. Através do operador * estamos acessando o conteúdo apontado pelos ponteiros e modificando-o. Mas, quem é este conteúdo? Nada mais que os valores armazenados em num1 e num2, que, portanto, estão sendo modificados! Espere um momento... será que nós já não vimos esta estória de chamar uma função com as variáveis precedidas de &? Já! É assim que nós chamamos a função scanf(). Mas por quê? Vamos pensar um pouco. A função scanf() usa chamada por referência porque ela precisa alterar as variáveis que passamos para ela! Não é para isto mesmo que ela é feita? Ela lê variáveis para nós e portanto precisa alterar seus valores. Por isto passamos para a função o endereço da variável a ser modificada! Exercício de Fixação Escreva uma função que receba duas variáveis inteiras e "zere" o valor das variáveis. Use o que você aprendeu nas páginas anteriores para fazer a implementação. RESOLUÇÃO: #include<stdio.h> 16 Programação de Computadores II Prof. Erlon Pinheiro #include<stdlib.h> void zera_numeros(int *n1, int *n2); main() { int num1, num2; //Entrada de dados printf(“Digite dois números inteiros:”); scanf(“%d%d”, &num1,&num2); //Processamento de dados zera_numeros(&num1,&num2); //Saída de dados printf(“\n\nnum1 = %d e num2 = %d\n\n“,num1,num2); system(“pause”); } void zera_numeros(int *n1, int *n2) { *n1 = 0; *n2 = 0; } Exercício de fixação parcialmente resolvido Desenvolver um programa calculadora que apresente um menu de seleções no programa principal. Esse menu deve dar ao usuário a possibilidade de escolher uma entre quatro operações aritméticas. Escolhida a opção desejada, deve ser solicitada à entrada de dois números, processada a operação, deve ser exibido o resultado. Algoritmo Note que este programa deve ser um conjunto de cinco módulos, sendo um principal e quatro secundários. O módulo principal efetuará o controle dos quatro módulos secundários que, por 17 Programação de Computadores II Prof. Erlon Pinheiro sua vez, efetuarão o pedido de leitura de dois valores, farão à operação e apresentarão o resultado obtido. Tendo uma idéia da estrutura do programa, será escrito em separado cada algoritmo com os seus detalhes de operação. Primeiro o programa principal e depois os outros módulos. Programa Principal 1. Apresentar um menu de seleções com cinco opções: 1. Adição 2. Subtração 3. Multiplicação 4. Divisão Inteira 5. Fim de Programa Escolha uma opção: 2. Armazenar a opção escolhida; 3. O módulo correspondente a opção escolhida deve ser executado; 4. Ao escolher o valor 5, o programa deve ser encerrado e caso o usuário escolha uma opção inválida, o programa deve exibir uma mensagem de erro e voltar ao menu inicial. Modulo 1 - Adição 1. Ler dois valores, no caso variáveis inteiras A e B; 2. Efetuar a soma das variáveis A e B, atribuindo o seu resultado na variável X; 3. Apresentar o valor da variável X; 4. Voltar ao programa Principal; Modulo 2 - Subtração 1. Ler dois valores,no caso variáveis inteiras A e B; 2. Efetuar a subtração das variáveis A e B, atribuindo o seu resultado na variável X; 3. Apresentar o valor da variável X; 4. Voltar ao programa Principal; Modulo 3 - Multiplicação 1. Ler dois valores, no caso variáveis inteiras A e B; 2. Efetuar a multiplicação das variáveis A e B, atribuindo o seu resultado na variável X; 3. Apresentar o valor da variável X; Modulo 4 – Divisão Inteira 18 Programação de Computadores II Prof. Erlon Pinheiro 1. Ler dois valores, no caso variáveis inteiras A e B; 2. Efetuar a divisão inteira das variáveis A e B, atribuindo o seu resultado na variável X; 3. Apresentar o valor da variável X; Faça o programa C referente a este algoritmo. 1.2.7 - Variáveis globais Variáveis globais são declaradas fora de todas as funções do programa. Elas são conhecidas e podem ser alteradas por todas as funções do programa. Quando uma função tem uma variável local com o mesmo nome de uma variável global a função dará preferência à variável local. Vamos ver um exemplo: int z,k; func1 (...) { int x,y; ... } func2 (...) { int x,y,z; ... z=10; ... } main () { int cont; ... } No exemplo acima as variáveis z e k são globais. Veja que func2() tem uma variável local chamada z. Quando temos então, em func2(), o comando z=10 quem recebe o valor de 10 é a variável local, não afetando o valor da variável global z. Na nossa disciplina NÃO será permitido o uso de variáveis globais. Elas ocupam memória o tempo todo (as locais só ocupam memória enquanto estão sendo usadas) e tornam o programa mais difícil de ser entendido e menos geral. As únicas variáveis que serão admitidas como globais serão os índices de vetores e matrizes. 1.2.8 - Ponteiros e Vetores Veremos nestas seções que ponteiros e vetores têm uma ligação muito forte. Quando você declara um vetor da seguinte forma: 19 Programação de Computadores II Prof. Erlon Pinheiro tipo_da_variável nome_da_variável [tam]; o compilador C calcula o tamanho, em bytes, necessário para armazenar este vetor. Este tamanho é: tam x tamanho_do_tipo O compilador então aloca este número de bytes em um espaço livre de memória. O nome da variável que você declarou é na verdade um ponteiro para o tipo da variável vetor. Este conceito é fundamental. Eis por que: Tendo alocado na memória o espaço para o vetor, ele toma o nome da variável (que é um ponteiro) e aponta para o primeiro elemento do vetor. Mas aí surge a pergunta: então como é que podemos usar a seguinte notação? nome_da_variável[índice] Isto pode ser facilmente explicado desde que você entenda que a notação acima é absolutamente equivalente a se fazer: *(nome_da_variável+ índice) Agora podemos entender como é que funciona um vetor! Vamos ver o que podemos tirar de informação deste fato. Fica claro, por exemplo, porque é que, no C, a indexação começa com zero. É porque, ao pegarmos o valor do primeiro elemento de um vetor, queremos, de fato, *nome_da_variável e então devemos ter um índice igual a zero. Então sabemos que: *nome_da_variável é equivalente a nome_da_variável[0] Outra coisa: apesar de, na maioria dos casos, não fazer muito sentido, poderíamos ter índices negativos. Estaríamos pegando posições de memória antes do vetor. Isto explica também porque o C não verifica a validade dos índices. Ele não sabe o tamanho do vetor. Ele apenas aloca a memória, ajusta o ponteiro do nome do vetor para o início do mesmo e, quando você usa os índices, encontra os elementos requisitados. Há uma diferença entre o nome de um vetor e um ponteiro que deve ser frisada: um ponteiro é uma variável, mas o nome de um vetor não é uma variável. Isto significa que não se consegue alterar o endereço que é apontado pelo "nome do vetor". Seja: const int tam = 10; int vetor[tam]; int *ponteiro, i; ponteiro = &i; /* as operações a seguir são inválidas */ vetor = vetor + 2; /* ERRADO: vetor não e' variavel */ vetor++; /* ERRADO: vetor não e' variavel */ vetor = ponteiro; /* ERRADO: vetor não e' variavel */ Teste as operações acima no seu compilador. Ele dará uma mensagem de erro. 20 Programação de Computadores II Prof. Erlon Pinheiro /* as operacoes abaixo sao validas */ ponteiro = vetor; /* CERTO: ponteiro e' variavel */ ponteiro = vetor+2; /* CERTO: ponteiro e' variavel */ Sabemos agora que, na verdade, o nome de um vetor é um ponteiro constante. Sabemos também que podemos indexar o nome de um vetor. Como conseqüência podemos também indexar um ponteiro qualquer. O programa mostrado a seguir funciona perfeitamente: #include <stdio.h> #include <stdlib.h> main () { const int tam = 10; int vetor [tam] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; int *p; p=vetor; printf ("O terceiro elemento do vetor e: %d",p[2]); system(“pause”); } Podemos ver que p[2] equivale a *(p+2) e a vetor[2]. O que você aprendeu nesta seção é de suma importância. Não siga adiante antes de entendê- la bem. 1.2.9 - Vetores como Parâmetros Seja o seguinte vetor: const int TAM = 50; int meuvetor[TAM]; Para passar o vetor meuvetor como parâmetro em uma função C, por exemplo, da função func(), tem-se que ter o parâmetro formal de func() declarado de uma das três maneiras apresentadas a seguir: void func(int vet[TAM]) ou void func(int vet[]) ou void func(int *vet) Nos três casos, tem-se em func() um int * chamado vet. Ao passar um vetor para uma função, na realidade se está passando um ponteiro. Nesse ponteiro é armazenado o endereço do primeiro elemento do vetor. Isto significa que não é feita uma cópia, elemento a elemento do 21 Programação de Computadores II Prof. Erlon Pinheiro vetor. Isso faz com que se possa alterar o valor dos elementos do vetor dentro da função (passagem por referência). Para simplificar podemos utilizar o primeiro modelo como padrão. Exemplo: Como que ficaria um programa (modularizado) que apenas leia um vetor de 15 números inteiros e apresente-o na tela? Resolução: #include <stdio.h> #include <stdlib.h> const int TAM = 15; int i; void leitura(int vet[TAM]);// ou void leitura(int *vet); void mostra_vet(int vet[TAM]); // void mostra_vet(int *vet); main () { int meuvetor[TAM]; //Entrada de dados printf ("Entrada de dados do vetor:\n\n”); leitura(meuvetor);//Note que passamos apenas o nome //Saída de dados printf(“\n\nO vetor lido:\n\n”); mostra_vet(meuvetor); system(“pause”); } void leitura(int vet[TAM])// void leitura(int *vet) { for(i=0;i<TAM;i++) { printf(“Digite o %do numero: “, i+1); scanf(“%d”, &vet[i]); } } void mostra_vet(int vet[TAM]) { for(i=0;i<TAM;i++) printf(“%5d”, vet[i]); printf(“\n\n”); } Obs.: Refaça os exercícios de Programação I para treinar modularização utilizando vetores. 22 Programação de Computadores II Prof. Erlon Pinheiro Exercícios Complementares 1 PROCURE PRIMEIRO PENSAR E RESOLVER OS EXERCÍCIOS EM PAPEL, E SÓ DEPOIS IMPLEMENTE E TESTE O PROGRAMA EM SEU COMPUTADOR. 1. Faça um programa que calcule e mostre o CÁLCULO DA HIPOTENUSA, sabendo as medidas dos catetos do triângulo retângulo. 2. Para cada item faça um programa modularizado C que: a) utilizando uma função calcule o arranjo de n elementos, p a p. Utilize fórmula A = n!/( n – p)!. A leitura de n e p deve ser feita na função main() e deve ser criado um módulo que retorne o valor de A. Deve-se utilizar o módulo que calcula o fatorial implementado em sala. b) utilizando uma função calcule o número de combinações de n elementos, p a p. Utilize fórmula C = n!/(p! * ( n – p)!). A leitura de n e p deve ser feitana função main() e deve ser criado um módulo que retorne o valor de C. c) calcule o somatório dos n primeiros termos da série de Fibonacci ( 1, 1, 2, 3, 5, 8, ...). A função deve receber o valor de n e retornar o valor do somatório para ser impresso na função principal. 3. Faça um programa C modularizado que leia 2 salários, identifique e imprima o maior. Use uma função para identificar e imprimir o maior salário. 4. Faça um programa C modularizado que calcule e mostre o valor da ÁREA DE UM QUADRADO, sabendo a medida do lado do quadrado. Faça uma versão deste problema para a área DE UM CÍRCULO. 5. Faça um programa C modularizado que calcule e mostre o CÁLCULO DA ÁREA DE UM TRIÂNGULO, sabendo a medida da base e da altura do triângulo. 6. Crie um programa C modularizado que simule uma CALCULADORA que apresente o menu abaixo e permita que o usuário efetue quantas e quais operações desejar com dois números lidos na opção 1 - Entrada de Dados. Ou seja, o usuário deve escolher a opção 1, digitar dois números e, em seguida, voltar ao menu para escolher qualquer das 4 operações. Após o usuário efetuar a escolha da operação, mostre o resultado e volte ao menu para que uma nova operação possa ser escolhida ou uma nova entrada de dados possa ser feita. 1 - Entrada de dados 2 - Adição 3 - Subtração 4 - Multiplicação 5 - Divisão 6 - Encerrar Escolha uma das operações: Para mostrar o menu de opções e ler a opção escolhida use uma função. Use funções para implementar as opções 2, 3, 4 e 5. Em cada função use como parâmetros os dois valores numéricos reais lidos na opção 1; os quais serão usados nas operações de adição, 23 Programação de Computadores II Prof. Erlon Pinheiro subtração, multiplicação ou divisão, dependendo da operação realizada pela função. Cada função deve retornar o resultado da operação realizada. Na operação de divisão o denominador não pode ser nulo. Use uma função para verificar este fato antes de efetuar a divisão. Esta função deve ter como parâmetro o denominador da divisão e retornar verdadeiro caso o denominador seja nulo e falso caso contrário. Resolução Parcial Algoritmo Note que este programa deve ser um conjunto de seis módulos, além da função principal. O módulo principal efetuará o controle dos seis módulos secundários que: (1º módulo) mostrará o menu, lerá e retornará a opção lida; (2º módulo) calculará e retornará a adição; (3 º módulo) calculará e retornará a subtração; (4 º módulo) calculará e retornará a multiplicação; (5 º módulo) calculará e retornará a divisão; (6 º módulo) verificará se o denominador é nulo. Tendo uma ideia da estrutura do programa, será escrito em separado cada algoritmo com os seus detalhes de operação. Primeiro o programa principal (main) e depois os outros módulos. Programa Principal 1. Chamar o módulo que apresenta um menu de seleções com seis opções: 1. Entrada de dados 2. Adição 3. Subtração 4. Multiplicação 5. Divisão 6. Fim de Programa Escolha uma opção: 2. Armazenar a opção escolhida; 3. O módulo correspondente a opção escolhida deve ser executado; 4. Ao escolher o valor 6, o programa deve ser encerrado e caso o usuário escolha uma opção inválida, o programa deve exibir uma mensagem de erro e voltar ao menu inicial (uso de um 24 Programação de Computadores II Prof. Erlon Pinheiro comando de repetição: de preferência o do/while). Observe que na opção 5 a função que verifica se o denominador é nulo deverá ser utilizada. Modulo 1 – Mostra o menu Apresentar o menu de opções; Ler a opção desejada; Retornar a opção para a função main; Modulo 2: Adição Efetuar a soma dos números lidos na função main; retornar o valor calculado; Voltar ao programa Principal = finalizar o módulo; Modulo 3 - Subtração Efetuar a subtração dos números lidos na função main; retornar o valor calculado; Voltar ao programa Principal = finalizar o módulo; Modulo 4 - Multiplicação Efetuar a multiplicação dos números lidos retornar o valor calculado; Voltar ao programa Principal = finalizar o módulo; Modulo 5 – Divisão Efetuar a divisão dos números lidos retornar o valor calculado; Voltar ao programa Principal = finalizar o módulo; Modulo 6: Verifica denominador nulo: Fica como exercício... Obs.: A verificação do denominador nulo será feita na função principal. 25 Programação de Computadores II Prof. Erlon Pinheiro Faça o programa C referente a este algoritmo. 7. Faça um programa que leia os limites de um intervalo de números inteiros, identifique, conte e imprima a quantidade de números pares e ímpares deste intervalo. Construir uma função para identificar, contar a quantidade de números pares e ímpares. A impressão deve ser feita na função main(). 8. Faça um programa C que utilizando uma função ordene três números inteiros (distintos) lidos na função principal main( ). Imprima os números ordenados em main( ). A função deve receber apenas os três parâmetros (ponteiros) para ordenar os números por referência. Dica: Podem usar variáveis locais para ajudar no processo de ordenação. 9. Faça uma função que verifique se um número inteiro, declarado como parâmetro da função, é divisível por 5. A função deve retornar 1 se sim e 0 caso contrário. 10. No algoritmo abaixo implemente a função MAIOR algoritmo imprime_maior; . : { algoritmo principal } inicio inteiro a, b, c; leia (a, b, c); escreva (“Maior número = “, MAIOR (a, b, c)); fim. 11. Escreva uma função que possua um número inteiro A como parâmetro e devolva o número de divisores de A. Para verificar o número de divisores de A teste, para todo x no intervalo [1, A/2]. Usando a função escrita acima, escreva outra função que possua um número inteiro N como parâmetro e determine se ele é ou não um número primo. N será primo se a função definida no parágrafo acima retornar o número de divisores igual a 1. 12. No algoritmo abaixo implemente a função F de forma que retorne o resultado da seguinte expressão: f(x) = x2 + 3x -9 algoritmo expressao; : {ALGORITMO PRINCIPAL} inicio real x; escreva(“Digite um numero real: “); leia (x); escreva (“f(x) = x2 + 3x –9 = “, f(x)); fim. 26 Programação de Computadores II Prof. Erlon Pinheiro 13. Seja o seguinte trecho de programa: int i=3,j=5; int *p, *q; p = &i; q = &j; Qual é o valor das seguintes expressões ? a) p == &i; b) *p - *q c) (*p)*(*q)== 20 d) 10* - (*p)/(*q)+7 14. Se i e j são variáveis inteiras e p e q ponteiros para inteiros devidamente inicializadas, quais das seguintes expressões de atribuição são ilegais? a) p = &i; b) *q = &j; c) p = q; d) i = *j; e) i = *&j; f) i = *&*&j; g) q = *p; h) i = (*p)++ + *q 15. Utilizando a elaboração de procedimentos, faça um programa que leia dois vetores de 10 elementos do tipo real. Intercale os elementos destes dois vetores criando um novo vetor de 20 elementos. Exiba o novo vetor. Exemplo: Vetor1 0 1 2 3 4 5 6 7 8 9 2 9 7 5.5 10 4 3 -1 6 21 Vetor2 0 1 2 3 4 5 6 7 8 9 32 -4 12 6 90 -4 5 43 27 82 Vetor criado a partir dos anteriores 0 1 2 3 4 5 6 7 8 9 1 0 1 1 1 2 1 3 1 4 1 5 1 6 1 7 1 8 19 2 3 2 9 -4 7 1 2 5.5 6 1 0 9 0 4 -4 3 5 -1 4 3 6 2 7 2 1 82 16. Utilizando a elaboração de procedimentos, faça um programa que tendo lido um vetor VET de 10 números inteiros diferentes de zero, crie um novo vetor VETPAR com os elementos pares do vetor VET. Antes de preencher o vetor VETPAR com os números pares de VET faça com que todos os componentes de VETPAR recebam o valor zero. Ao finalexiba VETPAR. Exemplo: 27 Programação de Computadores II Prof. Erlon Pinheiro Vetor VET 0 1 2 3 4 5 6 7 8 9 2 9 7 5 10 4 3 5 6 21 Vetor VETPAR 0 1 2 3 4 5 6 7 8 9 2 10 4 6 0 0 0 0 0 0 17. Uma mercearia trabalha com 10 mercadorias diferentes identificadas pelos números inteiros de 1 a 10. O dono da mercearia anota em uma tabela a quantidade de cada mercadoria vendida durante o dia. Ele possui uma outra tabela que indica para cada mercadoria o preço de venda. Um exemplo das tabelas é mostrado abaixo. Utilizando a elaboração de procedimentos, faça um programa que calcule e exiba o faturamento do dia da mercearia. Vetores exemplo: 0 1 2 3 4 5 6 7 8 9 Quantidade 23 41 7 23 23 45 6 8 211 112 0 1 2 3 4 5 6 7 8 9 Preço 10 5 2 12 23 1.5 6 5.55 3.50 1 Faturamento referente a mercadoria número 2 = 41*5 18. Faça um programa que leia 2 vetores, A e B, de tamanhos e tipos iguais e, construa um terceiro vetor C composto pela junção de A e B. Use módulos. 19. Uma Universidade deseja saber se existem alunos cursando simultaneamente as disciplinas Programação II e Lógica para Computação. Para tanto, coloque os números de matrícula dos alunos que cursam a disciplina Programação II em um vetor e os números de alunos que cursam Lógica para Computação em outro vetor. Exiba os números de matrícula que aparecem em ambos os vetores. Use procedimentos e funções. 28 Programação de Computadores II Prof. Erlon Pinheiro Unidade 2: Strings 2.1 - Introdução Strings são vetores de char s . Nada mais e nada menos. As strings são o uso mais comum para os vetores. Devemos apenas ficar atentos para o fato de que as strings têm o seu último elemento como um '\0'. A declaração geral para uma string é: char nome_da_string [tamanho]; Devemos lembrar que o tamanho da string deve incluir o '\0' final. A biblioteca padrão do C possui diversas funções que manipulam strings. Estas funções são úteis pois não se pode, por exemplo, igualar duas strings: string1==string2; /* NAO faca isto */ Fazer isto é um desastre. As strings devem ser igualadas elemento a elemento. Para armazenar uma string em C devemos reservar uma posição adicional para o caractere de fim da cadeia. Todas as funções de manipulação de strings em C recebem como parâmetro um vetor de char, isto é, um ponteiro para o primeiro elemento do vetor que representa a string. Ex.: para imprimir uma string utilizando o comando printf, utiliza-se o especificador de formato %s printf recebe um vetor de char e imprime elemento por elemento até encontrar o caractere nulo. Vantagem de delimitar a string por ‘\0’ não é necessário passar explicitamente o número de caracteres a ser considerado. main( ) { const int tam = 4; char faculdade[tam]; faculdade[0] = ‘U’; faculdade[1] = ‘V’; faculdade[2] = ‘V’; faculdade[3] = ‘\0’; printf(“%s \n”, faculdade); system(“pause”); } 29 Programação de Computadores II Prof. Erlon Pinheiro Se o caractere ‘\0’ não fosse colocado, a função printf seria executada de forma errada, pois não conseguiria identificar o final da cadeia. Como as strings são vetores, podemos reescrever o código anterior inicializando os elementos diretamente na declaração: main( ) { char faculdade[ ] = {‘U’,‘V’,‘V’,‘\0’}; /*a variável faculdade é automaticamente dimensionada e inicializada com 4 elementos.*/ printf(“%s \n”, faculdade); system(“pause”); } Exemplos de declarações e inicializações de strings: char s1[] = “”; //cadeia vazia (‘\0’) char s2[] = “Vila Velha”; //vetor de 11 elementos char s3[81]; /*string capaz de armazenar cadeias de até 80 elementos, com conteúdo desconhecido */ char s4[81] = “UVV”; /* também capaz de armazenar até 80 caracteres, mas seus primeiros 4 elementos já foram atribuídos na declaração. */ 2.2 – Leitura de Strings Em programação I vimos que a leitura de um caractere deve ser feita com cuidado, por exemplo: char a; ... scanf(“%c”, &a); ... 30 Programação de Computadores II Prof. Erlon Pinheiro Diferentemente dos especificadores %d e %f, o %c não pula os caracteres brancos (espaço, tabulação ou nova linha). Assim, este código possivelmente gerará erro quando for utilizado em programas maiores. O correto seria: char a; ... scanf(“ %c”, &a); /* o branco no formato pula brancos da entrada */ ... Para capturar uma string: char faculdade[81]; ... scanf(“%s”, faculdade); /* aqui não foi usado o caractere & pois a string é um vetor – o nome da variável representa o endereço do primeiro elemento do vetor */ Uso do especificador %s na leitura é limitado, pois ele pula os caracteres brancos, capturando somente a primeira seqüência de não-brancos. Como ler nomes compostos? Especificador %[...], onde especificamos entre os colchetes todos os caracteres que aceitaremos na leitura. Exemplo: o formato “%[aeiou]” lê seqüências de vogais, isto é a leitura prossegue até se encontrar um caractere que não seja vogal. Se o primeiro caractere for o acento circunflexo (^), teremos o efeito inverso (negação). Assim, “%[^aeiou]” faz a leitura enquanto uma vogal não for encontrada. char faculdade[81]; scanf(“ %[^\n]”, faculdade); /* lê seqüência de caracteres até que seja encontrado o caractere de mudança de linha (‘\n’), ou seja, captura-se até que ele tecle “Enter” */ Perigo: se o usuário fornecer uma linha com mais de 80 caracteres... invasão de espaço de memória não reservado! char faculdade[81]; scanf(“ %80[^\n]”, faculdade); /* lê no máximo 80 caracteres */ Exemplos: 31 Programação de Computadores II Prof. Erlon Pinheiro 2.3 – Criando funções que manipulam strings A impressão de uma variável string é feita como se ela fosse uma variável simples, isto é, basta usar a tag %s em um printf que a string será impressa. 1) É possível criar uma função que imprima o string caractere por caractere: void imprime(char *s) { int i; for (i=0; s[i] != ‘\0’; i++) printf(“%c”, s[i]); printf(“\n”); } 2) Esse código teria uma funcionalidade análoga à utilização do especificador de formato %s. void imprime (char* s) { printf(“%s\n”, s); } 3) Função para retornar o comprimento de uma string – basta contar o número de caracteres até o caractere nulo. int comprimento (char *s) { int i; int n = 0; /* contador */ for (i=0; s[i] != ‘\0’; i++) n++; return n; } 32 Programação de Computadores II Prof. Erlon Pinheiro O trecho de código a seguir faz uso desta função: main ( ) { int tam; char cidade[ ] = “Vila Velha”; tam = comprimento(cidade); printf(“A string \“ %s \“ tem %d caracteres\n”, cidade, tam); system(“pause”); } 4) Função que copia os elementos de uma string para outra: void copia (char *dest, char *orig) { int i; for(i=0; orig[i] != ‘\0’; i++) dest[i] = orig[i]; dest[i] = ‘\0’; /* fecha a cadeia copiada */ } 6) Função para concatenar uma string com outra já existente: void concatena (char* dest, char* orig) { int i=0; /* indice usado na cadeia de destino */ int j; /* indice usado na cadeia de origem */ /* acha o final do destino */ while (dest[i] != 0) i++; 33 Programação de Computadores II Prof. Erlon Pinheiro /* copia elementos da origem para o final do destino */ for (j=0; orig[j] != ‘\0’; j++) { dest[i] = orig[j]; i++; } dest[i] = ‘\0’; /* fecha a cadeia destino */ } 2.4 – Funções pré-definidas para manipulação de strings Vamos ver agora algumas funções básicas para manipulação de strings. a) gets A função gets( ) lê uma string do teclado. Sua forma geral é: gets (nome_da_string); O programaabaixo demonstra o funcionamento da função gets( ): #include <stdio.h> #include <stdlib.h> main ( ) { char nome[100]; printf ("Digite o seu nome: "); gets (nome); printf ("\n\n Ola %s", nome); system(“pause”); } b) puts puts imprime o conteúdo de uma string e desce para a próxima linha. Para imprimir a string do exemplo anterior bastava usar: puts(nome); 34 Programação de Computadores II Prof. Erlon Pinheiro c) strcpy Sua forma geral é: strcpy (string_destino,string_origem); A função strcpy() copia a string-origem para a string- destino. Seu funcionamento é semelhante ao da rotina apresentada na seção anterior. As funções apresentadas nestas seções estão no arquivo cabeçalho string.h. A seguir apresentamos um exemplo de uso da função strcpy(): #include <stdio.h> #include <stdlib.h> #include <string.h> main () { char str1[100],str2[100],str3[100]; printf ("Entre com uma string: "); gets (str1); strcpy (str2,str1); /* Copia str1 em str2 */ strcpy (str3,"Voce digitou a string "); /* Copia "Voce digitou a string" em str3 */ printf ("\n\n%s%s",str3,str2); system(“pause”); } d) strcat A função strcat() tem a seguinte forma geral: strcat (string_destino,string_origem); A string de origem permanecerá inalterada e será anexada ao fim da string de destino. Um exemplo: #include <stdio.h> #include <stdlib.h> #include <string.h> main () { char str1[100],str2[100]; printf ("Entre com uma string: "); gets (str1); strcpy (str2,"Voce digitou a string "); strcat (str2,str1); /* str2 armazenara' Voce digitou a string + o conteudo de str1 */ printf ("\n\n%s",str2); system(“pause”); } 35 Programação de Computadores II Prof. Erlon Pinheiro e) strlen Sua forma geral é: strlen (string); A função strlen() retorna o comprimento da string fornecida. O terminador nulo não é contado. Isto quer dizer que, de fato, o comprimento do vetor da string deve ser um a mais que o inteiro retornado por strlen(). Um exemplo do seu uso: #include <stdio.h> #include <stdlib.h> #include <string.h> main () { int comprimento; char frase[100]; printf ("Entre com uma frase: "); gets (frase); comprimento = strlen (frase); printf ("\n\nA frase que voce digitou tem tamanho %d", comprimento); system(“pause”); } f) strcmp Sua forma geral é: strcmp (string1,string2); A função strcmp() compara a string 1 com a string 2. Se as duas forem idênticas, a função retorna zero. Se elas forem diferentes a função retorna não-zero. Um exemplo da sua utilização: #include <stdio.h> #include <stdlib.h> #include <string.h> main () { char str1[100],str2[100]; printf ("Entre com uma string: "); gets (str1); printf ("\n\nEntre com outra string: "); gets (str2); if (strcmp(str1,str2)) // strcmp(str1,str2) != 0 printf ("\n\nAs duas strings são diferentes."); else printf ("\n\nAs duas strings são iguais."); system(“pause”); } 36 Programação de Computadores II Prof. Erlon Pinheiro g) toupper Esta é uma função para tornar um caractere alfabético no seu correspondente maiúsculo. Para utilizá-lo deve-se utilizar a biblioteca ctype, isto é, para utilizá-la no início do programa deve-se usar a diretiva: #include<ctype.h> A função toupper recebe um caractere e retorna o seu correspondente maiúsculo. Caso o caractere não seja uma letra válida a função mantém o caractere original. Exemplo: Considere a declaração abaixo: char car; car = toupper(`a`); Após a execução destas linhas a variável car conterá o caractere `A`. Exercício de Fixação Faça um programa que leia quatro palavras pelo teclado, e armazene cada palavra em uma string. Depois, concatene todas as strings lidas numa única string. Por fim apresente esta como resultado ao final do programa. Exercícios Complementares 2 1. Fazer um programa em "C" que lê uma string qualquer de no máximo 80 caracteres e imprime: a) Quantos caracteres tem o string; b) Quantos caracteres são de pontuação; c) Quantos caracteres são números; d) Quantos caracteres são espaço em branco. 2. Fazer um programa em "C" que lê uma string contendo palavras separadas por um espaço em branco cada e as imprime uma abaixo das outras. 3. Fazer um programa em "C" que pergunta o nome, o endereço, o telefone e a idade de uma pessoa e monta um string com a seguinte frase: "Seu nome é ..., você tem ... anos, mora na rua ... e seu telefone é ... ." 4. Fazer uma rotina que recebe uma string como parâmetro e imprime quantas palavras (separadas por espaços em branco) o mesmo contém. 5. Fazer uma rotina que aguarda (leia) uma string do teclado e retorna o valor 1 se a string digitada foi "SIM" e 0 se a string digitada foi "NAO". A rotina só deve retornar alguma coisa se a string digitada for "SIM" ou "NAO". 6. Implemente uma rotina que faça a mesma coisa que a função "strcpy". (essa foi feita em sala de aula...) 37 Programação de Computadores II Prof. Erlon Pinheiro 7. Fazer um programa em "C" que solicita um número inteiro e soletra o mesmo na tela. Ex: 124: um, dois, quatro 8. Escrever uma função que recebe uma string e um caractere como parâmetro e remove todas as ocorrências do caractere da string. 9. Escreva uma função em "C" que receba uma string, um caractere e o índice de uma posição válida da string como parâmetro e insira o caractere na posição "empurrando" todos os demais para o lado. 10. Faça um programa que leia uma frase de no máximo 80 caracteres e imprima o número de vogais presentes na frase. 11. Escreva um programa que leia um texto (de no máximo 70 caracteres) e mostre na tela o mesmo texto, duplicando cada uma das letras deste texto. Exemplo: texto lido: INSTITUTO DE INFORMATICA texto devolvido: IINNSSTTIITTUUTTOO DDEE IINNFFOORRMMAATTIICCAA. 12. Faça um programa que leia uma string e o codifique, utilizando a tabela de substituição de caracteres abaixo. Imprima a string original e a string decodificada. CARACTERE EXISTENTE CARACTERE A SER SUBSTITUÍDO M A N E P O Q I R U A M E N O P I Q U R 13. Faça um programa que leia uma string e o decodifique, utilizando a tabela de substituição de caracteres do exercício anterior. 38 Programação de Computadores II Prof. Erlon Pinheiro 14. Implemente um módulo que faça a mesma coisa que a função "strcmp". (O algoritmo foi falado em sala) 15 . Faça um módulo que receba um string e retorne a mesma string toda em maiúsculo. 39 Programação de Computadores II Prof. Erlon Pinheiro Unidade 3: Matrizes 3.1 – Introdução: Definições Iniciais Já vimos como declarar matrizes unidimensionais (vetores). Vamos tratar agora de matrizes bidimensionais. A forma geral da declaração de uma matriz bidimensional é muito parecida com a declaração de um vetor: tipo_da_variável nome_da_variável [altura][largura]; É muito importante ressaltar que, nesta estrutura, o índice da esquerda indexa as linhas e o da direita indexa as colunas. Quando vamos preencher ou ler uma matriz no C o índice mais à direita varia mais rapidamente que o índice à esquerda. Mais uma vez é bom lembrar que, na linguagem C, os índices variam de zero ao valor declarado, menos um; mas o C não vai verificar isto para o usuário. Manter os índices na faixa permitida é tarefa do programador. Abaixo damos um exemplo do uso de uma matriz: #include <stdio.h> #include <stdlib.h> main () { int mat [20][10]; int i,j,cont; cont=1; for (i=0;i<20;i++) for (j=0;j<10;j++) { mat[i][ j ]=cont; cont++; } system(“pause”); } No exemplo acima, a matriz mat é preenchida, sequencialmente por linhas, com os números de 1 a 200. Você deve entender o funcionamento do programa acima antes de prosseguir. 3.2 - Matrizes de strings Matrizes de stringssão matrizes bidimensionais. Imagine uma string. Ela é um vetor. Se fizermos um vetor de strings estaremos fazendo uma lista de vetores. Esta estrutura é uma matriz bidimensional de char s . Podemos ver a forma geral de uma matriz de strings como sendo: char nome_da_variável [num_de_strings][compr_das_strings]; Aí surge a pergunta: como acessar uma string individual? Fácil. É só usar apenas o primeiro índice. Então, para acessar uma determinada string faça: 40 Programação de Computadores II Prof. Erlon Pinheiro nome_da_variável [índice] Aqui está um exemplo de um programa que lê 5 strings e as exibe na tela: #include <stdio.h> #include <stdlib.h> main () { char strings [5][100]; int cont; for (cont=0;cont<5;cont++) { printf ("\n\nDigite uma string: "); gets (strings[cont]); } printf ("\n\n\nAs strings que voce digitou foram:\n\n"); for (cont=0;cont<5;cont++) printf ("%s\n",strings[cont]); system(“pause”); } 3.3 - Matrizes multidimensionais O uso de matrizes multidimensionais na linguagem C é simples. Sua forma geral é: tipo_da_variável nome_da_variável [tam1][tam2] ... [tamN]; Uma matriz N-dimensional funciona basicamente como outros tipos de matrizes. Basta lembrar que o índice que varia mais rapidamente é o índice mais à direita. 3.4 - Inicialização Podemos inicializar matrizes, assim como podemos inicializar variáveis. A forma geral de uma matriz como inicialização é: tipo_da_variável nome_da_variável [tam1][tam2] ... [tamN] = {lista_de_valores}; A lista de valores é composta por valores (do mesmo tipo da variável) separados por vírgula. Os valores devem ser dados na ordem em que serão colocados na matriz. Abaixo vemos alguns exemplos de inicializações de matrizes: float vect [6] = { 1.3, 4.5, 2.7, 4.1, 0.0, 100.1 }; int matrx [3][4] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; char str [10] = { 'J', 'o', 'a', 'o', '\0' }; char str [10] = "Joao"; char str_vect [3][10] = { "Joao", "Maria", "Jose" }; O primeiro demonstra inicialização de vetores. O segundo exemplo demonstra a inicialização de matrizes multidimensionais, onde matrx está sendo inicializada com 1, 2, 3 e 4 em sua primeira linha, 5, 6, 7 e 8 na segunda linha e 9, 10, 11 e 12 na última linha. No terceiro exemplo vemos 41 Programação de Computadores II Prof. Erlon Pinheiro como inicializar uma string e, no quarto exemplo, um modo mais compacto de inicializar uma string. O quinto exemplo combina as duas técnicas para inicializar um vetor de strings. Repare que devemos incluir o ; no final da inicialização. 3.4.1 - Inicialização sem especificação de tamanho Podemos, em alguns casos, inicializar matrizes das quais não sabemos o tamanho a priori. O compilador C vai, neste caso verificar o tamanho do que você declarou e considerar como sendo o tamanho da matriz. Isto ocorre na hora da compilação e não poderá mais ser mudado durante o programa, sendo muito útil, por exemplo, quando vamos inicializar uma string e não queremos contar quantos caracteres serão necessários. Alguns exemplos: char mess [] = "Linguagem C: flexibilidade e poder."; int matrx [][2] = { 1,2,2,4,3,6,4,8,5,10 }; No primeiro exemplo, a string mess terá tamanho 36. Repare que o artifício para realizar a inicialização sem especificação de tamanho é não especificar o tamanho! No segundo exemplo o valor não especificado será 5. Obs: Note que o número de linhas pode ser omitido, mas o número de coluna não! 42 Programação de Computadores II Prof. Erlon Pinheiro AUTO AVALIAÇÃO Veja como você está. O que imprime o programa a seguir? Tente entendê-lo e responder. A seguir, execute-o e comprove o resultado. # include <stdio.h> # include <stdlib.h> main() { int t, i, M[3][4]; for (t=0; t<3; t++) for (i=0; i<4; i++) M[t][i] = (t*4)+i+1; for (t=0; t<3; t++) { for (i=0; i<4; i++) printf (“%3d “, M[t][i]); printf (“\n”); } system(“pause”); } 3.5 – Passagem de Matrizes para Funções Após a declaração de uma matriz, a variável que representa a matriz é um ponteiro para o primeiro “vetor-linha” da matriz. Com isto, mat[1] aponta para o primeiro elemento do segundo “vetor-linha”, e assim por diante. Assim, uma matriz criada estaticamente é representada por um ponteiro para um “vetor-linha” com o número de elementos da linha. Quando passamos uma matriz para uma função, o parâmetro da função deve ser deste tipo. Infelizmente, a sintaxe para representar este tipo é obscura. O protótipo de uma função que recebe a matriz declarada acima seria: void f (..., float (*mat)[3], ...); Para uma matriz com 3 colunas Uma segunda opção é declarar o parâmetro como matriz, podendo omitir o número de linhas: void f (..., float mat[][3], ...); Ou ainda uma terceira opção seria declarar o parâmetro com o tamanho específico da matriz: void f (..., float mat[4][3], ...); De qualquer forma, o acesso aos elementos da matriz dentro da função é feito da forma usual, com indexação dupla. 3.6 – Tipos Básicos de Matrizes Matrizes Quadradas – São matrizes que possuem a quantidade de linhas igual a quantidade de colunas, formando um quadrado. 43 Programação de Computadores II Prof. Erlon Pinheiro Devido a essa propriedade essas matrizes apresentam características únicas comparadas às outras matrizes. Matriz Diagonal: elementos que podem ser diferentes de zero estão dispostos nas posições ocupadas por uma linha imaginária que corta a matriz na sua diagonal, do canto superior esquerdo ao canto inferior direito, o resto da matriz só pode conter zeros. Os elementos que ocupam essas posições possuem o número da sua linha igual ao número da sua coluna, sendo possível identificá-los em um trecho de programa através do comando de seleção: if (linha == coluna) Matriz Triangular Superior: Os elementos que estão dispostos nas posições ocupadas abaixo da diagonal principal devem (obrigatoriamente) ser iguais a zero, o resto da matriz pode ser não nula. Os elementos que ocupam essas posições possuem o número da sua linha menor que o número da coluna, sendo possível identificá-los em um trecho de programa através do comando: if (linha < coluna) Matriz Triangular Inferior: elementos que estão dispostos nas posições ocupadas abaixo da diagonal principal podem ser diferentes de zero. Os elementos que ocupam essas posições possuem o número da sua linha maior que o número da coluna, sendo possível identificá-los em um trecho de programa através do comando: if (linha > coluna) 44 9 -1 -1 0 0 0 0 0 0 0 0 0 0 0 0 0 9 1 1 00 1 00 0 0 0 0 8 4 3 9 1 1 00 0 00 0 0 0 0 0 4 31 9 Programação de Computadores II Prof. Erlon Pinheiro Matriz Diagonal Secundária: elementos que estão dispostos nas posições ocupadas por uma linha imaginária que corta a matriz na sua diagonal, do canto superior direito ao canto inferior esquerdo podem ser diferentes de zero, o resto da matriz só pode conter zeros. Os elementos que ocupam essas posições possuem o número da sua linha somado ao de sua coluna igual à dimensão de colunas menos uma unidade (lembre-se, em C a contagem inicia-se do zero), sendo possível identificálos em um trecho de programa (em C) através do comando: if (linha + coluna == qtde_colunas-1) No caso de matriz 4x4 do exemplo acima: if (linha + coluna == 3) Se essa condição for verdadeira, esse elemento estará na diagonal secundária. Exercícios Complementares 3 1. Escreva um programa em C que preencha uma matriz 4X4 com "0" e a sua diagonal principal com "1“. Em seguida, exiba a matriz na tela (uma linha da matriz por vez). Resolução: #include<stdio.h> #include<stdlib.h> const int
Compartilhar