Baixe o app para aproveitar ainda mais
Prévia do material em texto
392 Re vi sã o: Ju lia na - D ia gr am aç ão : J ef er so s / M ár ci o - 02 /0 6/ 20 14 Unidade VI Unidade VI Nesta unidade, abordaremos definitivamente a linguagem C. As noções de pseudocódigo e de todas as suas estruturas já foram ensinadas e sempre deverão ser usadas no desenvolvimento de programas. Passaremos a estudar técnicas e rotinas consagradas, que serão um atalho no desenvolvimento de programas mais complexos. Para o completo entendimento, necessitaremos entender como as linguagens, em especial a linguagem C, administram a memória RAM, o espaço disponível para os nossos programas serem executados. 6 AlocAção dinâmicA dA memóriA Ao declarar um vetor, para dimensioná-lo, era necessário saber de antemão quantos elementos iriam compô-lo. Tínhamos de prever o número máximo de elementos no vetor durante o processo da codificação. O pré-dimensionamento do vetor é um fator que limita a programação. Uma solução seria superdimensionar o vetor para tentar contornar essa falta de elementos livres durante a execução do programa, mas isso acarreta um desperdício de memória, o que é inaceitável em diversas aplicações, como aplicativos portáteis, em que sempre estamos sujeitos a ter falta de memória. A linguagem C oferece meios de utilizar e racionalizar o uso da memória durante a execução do programa, ou seja, podemos alocar memória dinamicamente. Esse recurso permite ao programa alocar elementos ou registros dinamicamente, sem desperdício de memória. 6.1 o uso da memória Basicamente, existem três maneiras de reservarmos espaço de memória para o armazenamento de informações, sem o rigor técnico. A primeira maneira é por meio do uso de variáveis globais (e estáticas). Nessa categoria de variáveis, o espaço reservado para uma variável existirá enquanto o programa estiver sendo executado. A segunda maneira é usando variáveis locais. Nessa categoria, o espaço na memória existe apenas no período em que a função que declarou a variável está sendo executada, sendo liberado assim que a execução da função terminar. Como consequência, a função que chama não pode fazer referência ao espaço local da função chamada. As variáveis globais ou as locais podem ser simples ou vetores. Para os 393 Re vi sã o: Ju lia na - D ia gr am aç ão : J ef er so s / M ár ci o - 02 /0 6/ 20 14 Linguagem e Técnicas de Programação vetores, é necessário informar o número máximo de elementos, caso contrário o compilador não terá a informação sobre o tamanho do espaço a ser reservado. A terceira maneira de reservar a memória é solicitar ao programa que aloque dinamicamente um espaço na memória durante sua execução. Esse espaço permanece reservado até que seja liberado explicitamente pelo programa, por meio de comando específico. Para o entendimento do processo de uso da memória, vamos fazer uma simulação da execução de um programa. 1. Vamos supor que esta seja uma memória RAM de um computador sem nenhum programa sendo executado: RAM Figura 442 – Memória RAM sem programa 2. Ao colocarmos um programa para ser executado, o sistema operacional carregará o código compilado na memória: 394 Re vi sã o: Ju lia na - D ia gr am aç ão : J ef er so s / M ár ci o - 02 /0 6/ 20 14 Unidade VI RAM Programa Figura 443 – Colocação do programa na memória 3. Uma vez com o código na memória, o sistema passa a ser comandado pelo programa. A primeira etapa é reservar o espaço para as constantes e as variáveis globais, pois essas são estáticas, ou seja, não serão criadas constantes nem variáveis globais durante a execução do resto do programa: 395 Re vi sã o: Ju lia na - D ia gr am aç ão : J ef er so s / M ár ci o - 02 /0 6/ 20 14 Linguagem e Técnicas de Programação RAM Programa Variáveis globais e constantes Figura 444 – Com o código na memória, o programa passa a comandar o sistema. 4. Cada vez que uma determinada função é chamada, o sistema reserva o espaço necessário para as variáveis locais da função. Simultaneamente, a pilha do controle da chamada das funções armazena a sequência de chamada e o estado geral das memórias operacionais. Conforme as funções são chamadas, a memória livre cresce, e a pilha é atualizada. Quando as funções são encerradas, o sistema consulta na pilha o ponto em que a função foi chamada, retornando o programa nesse ponto e liberando o espaço das variáveis e da pilha criados dinamicamente: 396 Re vi sã o: Ju lia na - D ia gr am aç ão : J ef er so s / M ár ci o - 02 /0 6/ 20 14 Unidade VI RAM Programa Variáveis locais Memória livre Pilha Variáveis globais e constantes Figura 445 – Comportamento do sistema quando as funções são chamadas e encerradas 6.2 Ponteiro de variáveis Na cidade de São Paulo existe um edifício famoso, o prédio da Gazeta, diante do qual, sempre que há uma comemoração da conquista de um campeonato, qualquer que seja o time paulista, a torcida se concentra, a fim de festejar. Vamos supor que você não seja da cidade de São Paulo, gostaria que comemorar o campeonato do seu time e queira chegar ao prédio da Gazeta a partir de um terminal de ônibus, de uma estação de trem ou de um aeroporto. Indo de táxi, pode pedir ao taxista para levá-lo ao prédio da Gazeta, ou então para a Avenida Paulista, nº 900. Pelo que vimos até agora, cada vez que uma variável é declarada, um espaço é alocado na memória para armazenar valores. Tecnicamente, é feito mais do que a simples reserva. O programa tem uma tabela, em que armazena o nome da variável, o endereço e o tipo de valor que será armazenado. Assim, ao escrevermos int a;, o programa reserva um espaço, normalmente de 4 bytes, por ser inteiro, no primeiro espaço livre da memória que encontrar. Em seguida, cria uma linha em uma tabela, na qual armazena o endereço e o tipo associado à variável criada nesse momento. Em muitas ocasiões, seria interessante poder acessar o conteúdo de um local da memória de outras maneiras que não fossem pelo nome ou pelo endereço de memória, por exemplo, da mesma maneira que o prédio da Gazeta pode ser localizado pelo seu endereço ou pelo seu nome. 397 Re vi sã o: Ju lia na - D ia gr am aç ão : J ef er so s / M ár ci o - 02 /0 6/ 20 14 Linguagem e Técnicas de Programação A linguagem C tem uma maneira especial de uma variável armazenar endereços. Essa variável se chama variável ponteiro, ou, simplesmente, ponteiro. A declaração de uma variável é feita da seguinte forma: <tipo> *nome; Por exemplo: int *p; O programa reserva um espaço na memória para uma variável chamada p, que irá guardar um endereço, e esse endereço armazenado conterá uma informação do tipo inteiro. O símbolo * identifica que uma variável é do tipo ponteiro. Para acessar os endereços de memória, a linguagem oferece dois operadores unários: • & (“endereço de”), aplicado a variáveis, resulta no endereço da posição da memória reservada para a variável; • * (“conteúdo de”), aplicado a variáveis do tipo ponteiro, acessa o conteúdo do endereço de memória armazenado pela variável ponteiro. Vamos analisar o seguinte trecho de programa: int a; int *p; a=5; p = &a; *p = 6; /*variável inteiro */ int a; /*variável ponteiro p/ inteiro */ int *p; /* a recebe o valor 5 */ a=5; /* p recebe o endereço de a (diz-se p aponta para a) */ p = &a; /* conteúdo de p recebe o valor 6 */ *p = 6; Memória RAM 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 398 Re visã o: Ju lia na - D ia gr am aç ão : J ef er so s / M ár ci o - 02 /0 6/ 20 14 Unidade VI Variáveis Figura 446 – Memória RAM livre Inicialmente, a memória RAM (nesse caso, uma memória fictícia) está livre, e o conteúdo pode ser qualquer sobra de outros programas que utilizaram esse espaço. /*variável inteiro */ int a; /*variável ponteiro p/ inteiro */ int *p; /* a recebe o valor 5 */ a=5; /* p recebe o endereço de a (diz-se p aponta para a) */ p = &a; /* conteúdo de p recebe o valor 6 */ *p = 6; Memória RAM 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 Variáveis a 101 int Figura 447 – Reserva de bytes e criação de tabela O programa reserva quatro bytes, em que serão armazenados os valores de a. Ao mesmo tempo, cria uma tabela com as referências da variável criada. /*variável inteiro */ int a; /*variável ponteiro p/ inteiro */ int *p; /* a recebe o valor 5 */ a=5; /* p recebe o endereço de a (diz-se p aponta para a) */ p = &a; /* conteúdo de p recebe o valor 6 */ *p = 6; 399 Re vi sã o: Ju lia na - D ia gr am aç ão : J ef er so s / M ár ci o - 02 /0 6/ 20 14 Linguagem e Técnicas de Programação Memória RAM 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 Variáveis a 101 int *p 105 int Figura 448 – Reserva de espaço para valores de p e atualização da tabela O programa reserva o espaço correspondente a um endereço de memória em que serão armazenados os valores de uma variável ponteiro p e atualiza a tabela. /*variável inteiro */ int a; /*variável ponteiro p/ inteiro */ int *p; /* a recebe o valor 5 */ a=5; /* p recebe o endereço de a (diz-se p aponta para a) */ p = &a; /* conteúdo de p recebe o valor 6 */ *p = 6; Memória RAM 101 102 103 104 105 106 107 108 109 110 5 111 112 113 114 115 116 117 118 119 120 Variáveis a 101 int *p 105 int Figura 449 – Atribuição de valor à variável a /*variável inteiro */ int a; /*variável ponteiro p/ inteiro */ int *p; /* a recebe o valor 5 */ a=5; /* p recebe o endereço de a (diz-se p aponta para a) */ p = &a; /* conteúdo de p recebe o valor 6 */ *p = 6; 400 Re vi sã o: Ju lia na - D ia gr am aç ão : J ef er so s / M ár ci o - 02 /0 6/ 20 14 Unidade VI Memória RAM 101 102 103 104 105 106 107 108 109 110 5 101 111 112 113 114 115 116 117 118 119 120 Variáveis a 101 int *p 105 int Figura 450 – O endereço é passado de a para p A variável p recebe endereço de a (note o operador unário &), passando a apontar para o endereço onde está a. /*variável inteiro */ int a; /*variável ponteiro p/ inteiro */ int *p; /* a recebe o valor 5 */ a=5; /* p recebe o endereço de a (diz-se p aponta para a) */ p = &a; /* conteúdo de p recebe o valor 6 */ *p = 6; Memória RAM 101 102 103 104 105 106 107 108 109 110 6 101 111 112 113 114 115 116 117 118 119 120 Variáveis a 101 int *p 105 int Figura 451 – O valor 6 é atribuído à variável a Aqui se encontra a grande diferença: no endereço apontado por p, receberá o valor 6, ou seja, a variável a passará a conter o valor 6. Nesse exemplo temos as duas maneiras pelas quais é possível trabalhar com memória em C: uma por meio do nome da variável, e a outra, pelo endereço desta. 6.3 Passagem de valores por valor e por referência Quando estudamos funções vimos que, quando passamos informações para dentro de uma função, fazemos isso por meio de parâmetros. Também foi dito que uma função é aquela que devolve um e apenas um valor, ou seja, não devolve mais de um valor. O uso de ponteiros amplia a possibilidade de utilização dos parâmetros e solução de problemas de retornos múltiplos. 401 Re vi sã o: Ju lia na - D ia gr am aç ão : J ef er so s / M ár ci o - 02 /0 6/ 20 14 Linguagem e Técnicas de Programação Dizemos que há uma passagem de parâmetros por valor quando conteúdos (e não endereços) são passados para as funções. No exemplo a seguir, os valores de a e b são passados para a função troca. #include<stdio.h> void troca(int a, int b){ int temp; temp=a; a=b; b=temp; } void main(){ int a=7,b=5; troca(a,b); printf(“\na=%d\nb=%d\n”,a,b); } Figura 452 – Saída mostrando a passagem de parâmetros por valor O resultado de a é 7 e o de b é 5, pois efetivamente as variáveis a e b são locais da função main( ). Quando os parâmetros passados são endereços, dizemos que foram passados por referência, como no exemplo a seguir. #include<stdio.h> void troca(int *a, int *b){ int temp; temp=*a; *a=*b; *b=temp; } void main(){ int a=7,b=5; troca(&a,&b); printf(“\na=%d\nb=%d\n”,a,b); } 402 Re vi sã o: Ju lia na - D ia gr am aç ão : J ef er so s / M ár ci o - 02 /0 6/ 20 14 Unidade VI Figura 453 – Saída do programa passando parâmetros por referência Apesar de os programas serem praticamente idênticos, o resultado é bem diferente, pois o mecanismo de trabalho foi bem diverso. Exemplo de aplicação Para entender o que acontece em cada um dos casos, vamos fazer o teste de mesa nos próprios códigos. • Passagem por valor #include<stdio.h> void troca(int a, int b){ int temp; temp=a; a=b; b=temp; } void main(){ int a=7,b=5; troca(a,b); printf(“\na=%d\nb=%d\n”,a,b); } Memória RAM 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 Variáveis:main Figura 454 – Início do programa 403 Re vi sã o: Ju lia na - D ia gr am aç ão : J ef er so s / M ár ci o - 02 /0 6/ 20 14 Linguagem e Técnicas de Programação #include<stdio.h> void troca(int a, int b){ int temp; temp=a; a=b; b=temp; } void main(){ int a=7,b=5; troca(a,b); printf(“\na=%d\nb=%d\n”,a,b); } Memória RAM 101 102 103 104 105 106 107 108 109 110 7 5 111 112 113 114 115 116 117 118 119 120 Variáveis:main a 101 int b 105 int Figura 455 – Criação das variáveis a e b As variáveis a e b da função são criadas e inicializadas. Nesse momento, o programa reserva os espaços na memória RAM e atualiza a tabela das variáveis da função main( ). #include<stdio.h> void troca(int a, int b){ int temp; temp=a; a=b; b=temp; } void main(){ int a=7,b=5; troca(a,b); printf(“\na=%d\nb=%d\n”,a,b); } 404 Re vi sã o: Ju lia na - D ia gr am aç ão : J ef er so s / M ár ci o - 02 /0 6/ 20 14 Unidade VI Memória RAM 101 102 103 104 105 106 107 108 109 110 7 5 111 112 113 114 115 116 117 118 119 120 7 5 Variáveis:main a 101 int b 105 int Variáveis:troca a 109 int b 113 int Figura 456 – Reserva de memória para as próximas variáveis e criação de tabela para as variáveis locais Ao encontrar a chamada para a função troca, o programa continua reservando espaço da memória para as variáveis que forem aparecendo e cria uma nova tabela para as suas variáveis locais, armazenando os parâmetros de entrada. Em seguida, passa a executar a função troca. #include<stdio.h> void troca(int a, int b){ int temp; temp=a; a=b; b=temp; } void main(){ int a=7,b=5; troca(a,b); printf(“\na=%d\nb=%d\n”,a,b); } Memória RAM 101 102 103 104 105 106 107 108 109 110 7 5 111 112 113 114 115 116 117 118119 120 7 5 Variáveis:main a 101 int b 105 int Variáveis:troca a 109 int b 113 int temp 117 int Figura 457 – O programa cria a variável temp 405 Re vi sã o: Ju lia na - D ia gr am aç ão : J ef er so s / M ár ci o - 02 /0 6/ 20 14 Linguagem e Técnicas de Programação #include<stdio.h> void troca(int a, int b){ int temp; temp=a; a=b; b=temp; } void main(){ int a=7,b=5; troca(a,b); printf(“\na=%d\nb=%d\n”,a,b); } Memória RAM 101 102 103 104 105 106 107 108 109 110 7 5 111 112 113 114 115 116 117 118 119 120 7 5 7 Variáveis:main a 101 int b 105 int Variáveis:troca a 109 int b 113 int temp 117 int Figura 458 – Temp recebe o conteúdo da variável a da função troca #include<stdio.h> void troca(int a, int b){ int temp; temp=a; a=b; b=temp; } void main(){ int a=7,b=5; troca(a,b); printf(“\na=%d\nb=%d\n”,a,b); } 406 Re vi sã o: Ju lia na - D ia gr am aç ão : J ef er so s / M ár ci o - 02 /0 6/ 20 14 Unidade VI Memória RAM 101 102 103 104 105 106 107 108 109 110 7 5 111 112 113 114 115 116 117 118 119 120 5 5 7 Variáveis:main a 101 int b 105 int Variáveis:troca a 109 int b 113 int temp 117 int Figura 459 – A variável a recebe o conteúdo da variável b da função troca #include<stdio.h> void troca(int a, int b){ int temp; temp=a; a=b; b=temp; } void main(){ int a=7,b=5; troca(a,b); printf(“\na=%d\nb=%d\n”,a,b); } Memória RAM 101 102 103 104 105 106 107 108 109 110 7 5 111 112 113 114 115 116 117 118 119 120 5 7 7 Variáveis:main a 101 int b 105 int Variáveis:troca a 109 int b 113 int temp 117 int Figura 460 – A variável b recebe conteúdo de temp A variável b recebe o conteúdo da variável temp da função troca, e o programa retorna para o ponto de chamada da função main( ). 407 Re vi sã o: Ju lia na - D ia gr am aç ão : J ef er so s / M ár ci o - 02 /0 6/ 20 14 Linguagem e Técnicas de Programação #include<stdio.h> void troca(int a, int b){ int temp; temp=a; a=b; b=temp; } void main(){ int a=7,b=5; troca(a,b); printf(“\na=%d\nb=%d\n”,a,b); } Memória RAM 101 102 103 104 105 106 107 108 109 110 7 5 111 112 113 114 115 116 117 118 119 120 5 7 7 Variáveis:main a 101 int b 105 int Variáveis:troca a 109 int b 113 int temp 117 int Tela: a=7 b=5 Figura 461 – Exibição dos conteúdos de a e b da função main( ) A função main( ) exibe os conteúdos das variáveis a e b da própria função main( ), aqueles valores que estão nos endereços da tabela Variáveis:main. Passagem por referência #include<stdio.h> void troca(int *a, int *b){ int temp; temp=*a; *a=*b; *b=temp; } void main(){ int a=7,b=5; troca(&a,&b); printf(“\na=%d\nb=%d\n”,a,b); } 408 Re vi sã o: Ju lia na - D ia gr am aç ão : J ef er so s / M ár ci o - 02 /0 6/ 20 14 Unidade VI Memória RAM 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 Variáveis:main Figura 462 – Início do programa O programa começa com a memória vazia, executando a função main( ). #include<stdio.h> void troca(int *a, int *b){ int temp; temp=*a; *a=*b; *b=temp; } void main(){ int a=7,b=5; troca(&a,&b); printf(“\na=%d\nb=%d\n”,a,b); } Memória RAM 101 102 103 104 105 106 107 108 109 110 7 5 111 112 113 114 115 116 117 118 119 120 Variáveis:main a 101 int b 105 int Figura 463 – Criação e inicialização das variáveis a e b As variáveis a e b da função são criadas e inicializadas. Nesse momento, o programa reserva os espaços na memória RAM e atualiza a tabela das variáveis da função main( ). Até esse ponto, os dois programas são iguais. #include<stdio.h> void troca(int *a, int *b){ int temp; temp=*a; *a=*b; *b=temp; } void main(){ 409 Re vi sã o: Ju lia na - D ia gr am aç ão : J ef er so s / M ár ci o - 02 /0 6/ 20 14 Linguagem e Técnicas de Programação int a=7,b=5; troca(&a,&b); printf(“\na=%d\nb=%d\n”,a,b); } Memória RAM 101 102 103 104 105 106 107 108 109 110 7 5 &101 111 112 113 114 115 116 117 118 119 120 &105 Variáveis:main a 101 int b 105 int Variáveis:troca *a 109 int *b 111 int Figura 464 – Recebimento dos ponteiros Na chamada da função é que começam as diferenças. A função troca recebe dois ponteiros do tipo inteiro, lembrando sempre que ponteiros armazenam endereços, e não valores absolutos. Na passagem de parâmetro, são endereços de memória, em virtude do operador unário & aplicado em a e em b. #include<stdio.h> void troca(int *a, int *b){ int temp; temp=*a; *a=*b; *b=temp; } void main(){ int a=7,b=5; troca(&a,&b); printf(“\na=%d\nb=%d\n”,a,b); } Memória RAM 101 102 103 104 105 106 107 108 109 110 7 5 &101 111 112 113 114 115 116 117 118 119 120 &105 Variáveis:main a 101 int b 105 int Variáveis:troca *a 109 int *b 111 int temp 113 int Figura 465 – O programa cria normalmente a variável temp da função troca 410 Re vi sã o: Ju lia na - D ia gr am aç ão : J ef er so s / M ár ci o - 02 /0 6/ 20 14 Unidade VI #include<stdio.h> void troca(int *a, int *b){ int temp; temp=*a; *a=*b; *b=temp; } void main(){ int a=7,b=5; troca(&a,&b); printf(“\na=%d\nb=%d\n”,a,b); } Memória RAM 101 102 103 104 105 106 107 108 109 110 7 5 &101 111 112 113 114 115 116 117 118 119 120 &105 7 Variáveis:main a 101 int b 105 int Variáveis:troca *a 109 int *b 111 int temp 113 int Figura 466 – Chamada da função Na chamada da função é que começam as diferenças. #include<stdio.h> void troca(int *a, int *b){ int temp; temp=*a; *a=*b; *b=temp; } void main(){ int a=7,b=5; troca(&a,&b); printf(“\na=%d\nb=%d\n”,a,b); } 411 Re vi sã o: Ju lia na - D ia gr am aç ão : J ef er so s / M ár ci o - 02 /0 6/ 20 14 Linguagem e Técnicas de Programação Memória RAM 101 102 103 104 105 106 107 108 109 110 7 5 &101 111 112 113 114 115 116 117 118 119 120 &105 7 Variáveis:main A 101 int B 105 int Variáveis:troca *a 109 int *b 111 int temp 113 int Figura 467 – Armazenamento do valor 7 em temp A variável temp recebe o conteúdo apontado pela variável a da função troca. Como a variável a aponta para o endereço 101 e é do tipo inteiro, o valor 7 é armazenado em temp. #include<stdio.h> void troca(int *a, int *b){ int temp; temp=*a; *a=*b; *b=temp; } void main(){ int a=7,b=5; troca(&a,&b); printf(“\na=%d\nb=%d\n”,a,b); } Memória RAM 101 102 103 104 105 106 107 108 109 110 5 5 &101 111 112 113 114 115 116 117 118 119 120 &105 7 Variáveis:main A 101 int B 105 int Variáveis:troca a 109 int b 111 int temp 113 int Figura 468 – O endereço &101 recebe o valor do endereço &105 O endereço apontado pela variável a da função troca recebe o conteúdo apontado pela variável b da função troca, ou seja, no endereço &101, receberá o valor que está no endereço&105. 412 Re vi sã o: Ju lia na - D ia gr am aç ão : J ef er so s / M ár ci o - 02 /0 6/ 20 14 Unidade VI #include<stdio.h> void troca(int *a, int *b){ int temp; temp=*a; *a=*b; *b=temp; } void main(){ int a=7,b=5; troca(&a,&b); printf(“\na=%d\nb=%d\n”,a,b); } Memória RAM 101 102 103 104 105 106 107 108 109 110 5 5 &101 111 112 113 114 115 116 117 118 119 120 &105 7 Variáveis:main A 101 int B 105 int Variáveis:troca a 109 int b 111 int temp 113 int Figura 469 – O endereço &105 recebe o valor 7 do temp O endereço apontado pela variável b da função troca recebe o conteúdo da variável temp dessa função, ou seja, no endereço &105, receberá o valor 7 do temp. O programa retorna para o ponto em que foi chamado. #include<stdio.h> void troca(int *a, int *b){ int temp; temp=*a; *a=*b; *b=temp; } void main(){ int a=7,b=5; troca(&a,&b); printf(“\na=%d\nb=%d\n”,a,b); } 413 Re vi sã o: Ju lia na - D ia gr am aç ão : J ef er so s / M ár ci o - 02 /0 6/ 20 14 Linguagem e Técnicas de Programação Memória RAM 101 102 103 104 105 106 107 108 109 110 5 5 &101 111 112 113 114 115 116 117 118 119 120 &105 7 Variáveis:main A 101 int B 105 int Variáveis:troca a 109 int b 111 int temp 113 int Tela: a=5 b=7 Figura 470 – As variáveis a e b da função principal estão com os valores trocados Nesse exemplo, vimos que a passagem de valor por referência permite que variáveis criadas em uma função possam ser alteradas fora dela. Essa técnica se torna importante, principalmente, pelo fato de a linguagem C não passar vetores e matrizes como parâmetros de uma função. 6.4 Passagem de vetores para funções Como dito, a linguagem C não passa vetores como parâmetros de função; assim, a maneira que temos para fazê-lo é passando o endereço da primeira posição do vetor. Dessa forma, para receber um vetor, devemos colocar uma declaração do mesmo tipo do vetor com a indicação de que é um ponteiro (usando o *). Exemplo de aplicação Dado o programa a seguir: include <stdio.h> void incrvetor (int n, int *v) { int i; for (i = 0; i < n; i++) v[i]++; } void main ( ) { int a[ ] = {1, 3, 5}; incrvetor(3, a); printf(“%d %d %d \n”, a[0], a[1], a[2]); } 414 Re vi sã o: Ju lia na - D ia gr am aç ão : J ef er so s / M ár ci o - 02 /0 6/ 20 14 Unidade VI Na função main() é criado o vetor inteiro a[], que será passado como parâmetro para a função incvetor(). Assim, a função recebe o endereço do vetor a pela declaração int * v. Nesse caso, o vetor é chamado de a na função main() e, dentro da função incvetor(), é tratado como v. Como ambos têm o mesmo endereço, ambos se referem ao mesmo espaço de memória. O resultado desse programa é: Figura 471 – Saída do programa com passagem de vetor na função O programa passa como parâmetros um número e um vetor; este tem como elementos os valores {1, 3, 5}. A função recebe o endereço do vetor, refere-se a ele como v e acrescenta 1 a cada elemento; assim, o vetor é alterado para {2, 4, 6}. lembrete As bibliotecas de funções são anexadas ao programa principal pela diretiva de pré-compilação #include. Vimos até agora as bibliotecas stdio.h e string.h. 6.5 Funções da biblioteca-padrão Muitas vezes, o uso de vetores e matrizes fica limitado pela necessidade de sabermos antecipadamente a quantidade de elementos que serão necessários. O interessante seria termos condições de criar e destruir elementos sem a limitação (apenas da máquina, e não da lógica), conforme forem surgindo as necessidades de uso. A biblioteca stdlib.h possui algumas funções que nos permitem criar e trabalhar dinamicamente, ou seja, durante a execução de um certo trecho do programa. Para isso, precisamos entender algumas funções e utilizar os conhecimentos do uso da memória que vimos até agora. A melhor forma de usarmos todas as funções necessárias a partir deste momento do curso é mostrando que, fazendo manualmente, o trecho int *v; v = (int*) malloc(10*sizeof(int)); 415 Re vi sã o: Ju lia na - D ia gr am aç ão : J ef er so s / M ár ci o - 02 /0 6/ 20 14 Linguagem e Técnicas de Programação é equivalente a simplesmente declararmos: int v[10]; Para estudarmos com detalhes o trecho, vamos analisar minuciosamente como é escrita a segunda linha. Começamos com a memória fictícia vazia; temos o endereço começando do 101 e, logo abaixo, o seu byte. 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 Variáveis: nome endereço tipo Figura 472 – Memória fictícia vazia, começando do 101 Para termos uma referência: int *v; 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 Variáveis: *v 101 int Figura 473 – O ponteiro v é alocado na posição 101 416 Re vi sã o: Ju lia na - D ia gr am aç ão : J ef er so s / M ár ci o - 02 /0 6/ 20 14 Unidade VI A primeira função é o malloc(int), memory allocation ou alocação de memória. A função malloc reserva a quantidade de bytes que é passada como parâmetro e retorna o endereço em que esse espaço de memória foi reservado. Assim, se quisermos reservar o espaço de dez elementos inteiros para um vetor, imaginando que cada elemento de inteiro use 2 bytes (esse é um número puramente fictício, pois cada compilador pode ter um tamanho diferente), poderemos reservar 20 bytes de memória. malloc(20) Considerando que 20 é 10* 2, dez elementos de 2 bytes. A função devolverá o valor &103, que é o endereço em que o espaço foi alocado. 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 Variáveis: *v 101 int Figura 474 Neste momento, se fizermos v=malloc(20); o programa irá acusar erro, pois existe um conflito de informações: a variável v é um ponteiro apontando para inteiro, e o espaço de memória não corresponde à informação de um valor inteiro. Voltando ao número 20, não sabemos ao certo se a quantidade de bytes que uma variável do tipo inteiro ocupa é realmente 2 bytes. Para descobrir quantos bytes um tipo ocupa, a biblioteca nos fornece a função sizeof(tipo), tamanho de. Essa função retorna um número inteiro que um tipo ocupa, seja ele preexistente, seja criado por nós com otypedef. 417 Re vi sã o: Ju lia na - D ia gr am aç ão : J ef er so s / M ár ci o - 02 /0 6/ 20 14 Linguagem e Técnicas de Programação Assim, por exemplo, se fizermos sizeof( int ) ele devolverá a quantidade de bytes utilizados por uma variável do tipo int. Então, se quisermos um vetor de 10 elementos, inteiros, bastará fazer: 10 x sizeof(int) Assim, se colocarmos malloc(10*sizeof(int)) teremos o tamanho correto para alocarmos 10 números inteiros. 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 Variáveis: *v 101 int Figura 475 – Alocados dez números inteiros Agora o espaço alocado está correto, mas a expressão v= malloc(10*sizeof(int)) continua acusando erro, pois v é um ponteiro inteiro, e esse espaço alocado não corresponde ao de inteiro. Vimos na Unidade II um item chamado conversão de tipos, o chamado cast. Agora, isso será útil, pois transforma qualquer coisa em um tipo de variável. Nesse caso, servirá para formatar ou adequar o espaço alocado. Como o tipo de v é um ponteiro inteiro, aplicaremos a conversão do tipo int *. 418 Re vi sã o: Ju lia na - D ia gr am aç ão : J ef er so s / M ár ci o - 02 /0 6/ 20 14 Unidade VI Assim, a expressão fica: v=(int *)malloc(10*sizeof(int)); Com isso, a informação devolvida pelo malloc fica completa: um endereço e o tipo correspondente. Ao aplicarmos o cast, a memória ficará assim: 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 &103 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 Variáveis: *v 101 int Figura 476 – Cast aplicado Para liberar um espaço de memória alocado dinamicamente, utiliza-se a função free da biblioteca stilib.h. Essa função recebe como parâmetro o ponteiro da memória a ser liberada, e o espaço alocado é liberado para outros usos futuros. Assim, para liberar o vetor v, fazemos: free(v); observação Os ponteiros também têm uma aritmética própria. Ao fazermos uma soma de um número inteiro a um ponteiro, este apontará para o endereço com o avanço de múltiplos correspondente ao tamanho do tipo definido para ele. 419 Re vi sã o: Ju lia na - D ia gr am aç ão : J ef er so s / M ár ci o - 02 /0 6/ 20 14 Linguagem e Técnicas de Programação Assim, no exemplo anterior, podemos ter as seguintes equivalências: Tabela 12 – Relação entre o ponteiro e o índice de um vetor Expressão Fazer a expressão Equivale a fazer a expressão int *x = v+0 printf(“%d”,*x) printf(“%d”,v[0]) int *x = v+1 printf(“%d”,*x) printf(“%d”,v[1]) int *x = v+2 printf(“%d”,*x) printf(“%d”,v[2]) int *x = v+3 printf(“%d”,*x) printf(“%d”,v[3]) int *x = v+4 printf(“%d”,*x) printf(“%d”,v[4]) 6.6 recursividade Vimos que os vetores são limitados porque, desde o começo, precisamos saber a sua dimensão, e tivemos como solução o uso da alocação dinâmica da memória, para casos em que, a cada execução do programa, as suas dimensões podem mudar. Agora existem casos em que não só a memória, mas também o próprio programa tem de se adaptar a uma situação diferente a cada momento. Não é prático alterar o programa antes de cada execução. Para solucionar alguns desses problemas, podemos utilizar a recursividade. Existem algumas situações que só podem ser contornadas por meio dela. A recursividade é a possibilidade de uma função se chamar, ou, então, voltar a ser chamada após a execução de funções que ela mesma chamou. Isso quer dizer que, a cada chamada, o programa abre um novo espaço na memória para a sua execução. A cada chamada recursiva, é guardada uma cópia dos parâmetros, de modo que não percamos os valores dos parâmetros das chamadas anteriores. Em princípio, enquanto houver memória para a recursividade, a função poderá ser reutilizada. As funções recursivas têm duas características importantes: • ponto de parada: geralmente, um limite superior ou inferior da regra geral; • regra geral: reduz a resolução do problema por meio da invocação recursiva de casos menores, resolvidos mediante a resolução de casos ainda menores, e assim sucessivamente, até atingir o ponto de parada, que finaliza o método. As funções que não são recursivas são chamadas de iterativas. Exemplo de aplicação Multiplicação de inteiro: #include <stdio.h> int mult(int x, int y){ if (y == 1){ 420 Re vi sã o: Ju lia na - D ia gr am aç ão : J ef er so s / M ár ci o - 02 /0 6/ 20 14 Unidade VI return(x); }else{ return(x + mult(x, y-1)); } } void main ( ) { printf(“\n %d x %d = % d”, 2,3,mult(2,3)); } mult(2,3) 2+mult(2,2) 2+mult(2,1) 2+ 2 2+ 4 6 Figura 477 – Modelo de chamada e retorno recursivos A função se chama sucessivamente e, a cada chamada, o valor do parâmetro y decresce, até chegar ao ponto de parada, (y==1), e passa a ter retornos sucessivos, até a primeira chamada. Fazendo o teste de mesa, temos: #include <stdio.h> int mult(int x, int y){ if (y == 1){ return(x); }else{ return(x + mult(x, y-1)); } } void main ( ) { printf(“\n %d x %d = % d”, 2,3,mult(2,3)); } 421 Re vi sã o: Ju lia na - D ia gr am aç ão : J ef er so s / M ár ci o - 02 /0 6/ 20 14 Linguagem e Técnicas de Programação #include <stdio.h> int mult(int x, int y){ } void main ( ) { printf(“\n %d x %d = % d”, 2,3,mult(2,3)); } Memória RAM 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 Variáveis:main mult(2,3) Figura 478 – A função main chama a função mult( ), passando como parâmetros 2 e 3 #include <stdio.h> void main ( ) { printf(“\n %d x %d = % d”, 2,3,mult(2,3)); } int mult(int x, int y){ if (y == 1){ return(x); }else{ return(x + mult(x, y-1)); } } Memória RAM 101 102 103 104 105 106 107 108 109 110 2 3 111 112 113 114 115 116 117 118 119 120 Variáveis:main mult(2,3) Variáveis:mult x 101 int y 103 int Figura 479 – A função mult( ) é executada, criando na memória as variáveis x e y e recebendo os valores passados pela função main( ) 422 Re vi sã o: Ju lia na - D ia gr am aç ão : J ef er so s / M ár ci o - 02 /0 6/ 20 14 Unidade VI #include <stdio.h> void main ( ) { printf(“\n %d x %d = % d”, 2,3,mult(2,3)); } int mult(int x, int y){ if (y == 1){ return(x); }else{ return(x + mult(x, y-1)); } } Memória RAM 101 102 103 104 105 106 107 108 109 110 2 3 111 112 113 114 115 116 117 118 119 120 Variáveis:main mult(2,3) Variáveis:mult x 101 int y 103 int Figura 480 – Como y (na posição 103) não é 1, o programa passa para o else #include <stdio.h> void main ( ) { printf(“\n %d x %d = % d”, 2,3,mult(2,3)); } int mult(int x, int y){if (y == 1){ return(x); }else{ return(x + mult(x, y-1)); } } 423 Re vi sã o: Ju lia na - D ia gr am aç ão : J ef er so s / M ár ci o - 02 /0 6/ 20 14 Linguagem e Técnicas de Programação Memória RAM 101 102 103 104 105 106 107 108 109 110 2 3 2 2 111 112 113 114 115 116 117 118 119 120 Variáveis:main mult(2,3) Variáveis:mult x 101 int y 103 int mult(2,2) Figura 481 – Executando a função mult( ) No else, a primeira operação a ser executada é a chamada função mult( ), agora com os parâmetros x=2 e y-1, ou seja, 2. #include <stdio.h> void main ( ) { printf(“\n %d x %d = % d”, 2,3,mult(2,3)); } int mult(int x, int y){ if (y == 1){ return(x); }else{ return(x + mult(x, y-1)); } } int mult(int x, int y){ if (y == 1){ return(x); }else{ return(x + mult(x, y-1)); } } 424 Re vi sã o: Ju lia na - D ia gr am aç ão : J ef er so s / M ár ci o - 02 /0 6/ 20 14 Unidade VI Memória RAM 101 102 103 104 105 106 107 108 109 110 2 3 2 2 111 112 113 114 115 116 117 118 119 120 Variáveis:main mult(2,3) Variáveis:mult x 101 int y 103 int mult(2,2) Variáveis:mult x 105 int y 107 int Figura 482 – Uma nova função mult( ) é aberta, e são criadas as variáveis x e y na memória #include <stdio.h> void main ( ) { printf(“\n %d x %d = % d”, 2,3,mult(2,3)); } int mult(int x, int y){ if (y == 1){ return(x); }else{ return(x + mult(x, y-1)); } } int mult(int x, int y){ if (y == 1){ return(x); }else{ return(x + mult(x, y-1)); } } 425 Re vi sã o: Ju lia na - D ia gr am aç ão : J ef er so s / M ár ci o - 02 /0 6/ 20 14 Linguagem e Técnicas de Programação Memória RAM 101 102 103 104 105 106 107 108 109 110 2 3 2 2 111 112 113 114 115 116 117 118 119 120 Variáveis:main mult(2,3) Variáveis:mult x 101 int y 103 int mult(2,2) Variáveis:mult x 105 int y 107 int Figura 483 – Como y vale 2, o else é executado #include <stdio.h> void main ( ) { printf(“\n %d x %d = % d”, 2,3,mult(2,3)); } int mult(int x, int y){ if (y == 1){ return(x); }else{ return(x + mult(x, y-1)); } } int mult(int x, int y){ if (y == 1){ return(x); }else{ return(x + mult(x, y-1)); } } 426 Re vi sã o: Ju lia na - D ia gr am aç ão : J ef er so s / M ár ci o - 02 /0 6/ 20 14 Unidade VI Memória RAM 101 102 103 104 105 106 107 108 109 110 2 3 2 2 111 112 113 114 115 116 117 118 119 120 Variáveis:main mult(2,3) Variáveis:mult x 101 int y 103 int mult(2,2) Variáveis:mult x 105 int y 107 int mult(2,1) Figura 484 – Uma nova instância (rotina) do mult é chamada, agora com os parâmetros 2 e 1 #include <stdio.h> void main ( ) { printf(“\n %d x %d = % d”, 2,3,mult(2,3)); } int mult(int x, int y){ if (y == 1){ return(x); }else{ return(x + mult(x, y-1)); } } int mult(int x, int y){ if (y == 1){ return(x); }else{ return(x + mult(x, y-1)); } } int mult(int x, int y){ if (y == 1){ return(x); }else{ return(x + mult(x, y-1)); } } 427 Re vi sã o: Ju lia na - D ia gr am aç ão : J ef er so s / M ár ci o - 02 /0 6/ 20 14 Linguagem e Técnicas de Programação Memória RAM 101 102 103 104 105 106 107 108 109 110 2 3 2 2 2 111 112 113 114 115 116 117 118 119 120 1 Variáveis:main mult(2,3) Variáveis:mult x 101 int y 103 int mult(2,2) Variáveis:mult x 105 int y 107 int mult(2,1) Variáveis:mult x 109 int y 111 int Figura 485 – Na nova instância, novos x e y são criados na memória #include <stdio.h> void main ( ) { printf(“\n %d x %d = % d”, 2,3,mult(2,3)); } int mult(int x, int y){ if (y == 1){ return(x); }else{ return(x + mult(x, y-1)); } } int mult(int x, int y){ if (y == 1){ return(x); }else{ return(x + mult(x, y-1)); } } int mult(int x, int y){ if (y == 1){ return(x); 428 Re vi sã o: Ju lia na - D ia gr am aç ão : J ef er so s / M ár ci o - 02 /0 6/ 20 14 Unidade VI }else{ return(x + mult(x, y-1)); } } Memória RAM 101 102 103 104 105 106 107 108 109 110 2 3 2 2 2 111 112 113 114 115 116 117 118 119 120 1 Variáveis:main mult(2,3) Variáveis:mult x 101 int y 103 int mult(2,2) Variáveis:mult x 105 int y 107 int 2 Variáveis:mult x 109 int y 111 int Figura 486 – Execução do return Como o y chegou ao 1, dizemos que chegou ao ponto de parada, em que a função não é mais chamada recursivamente. Nesse ponto, o programa executa o return(x), ou seja, retorna o conteúdo de x, que é 2, para a função que o chamou. #include <stdio.h> void main ( ) { printf(“\n %d x %d = % d”, 2,3,mult(2,3)); } int mult(int x, int y){ if (y == 1){ return(x); }else{ return(x + mult(x, y-1)); } } int mult(int x, int y){ if (y == 1){ 429 Re vi sã o: Ju lia na - D ia gr am aç ão : J ef er so s / M ár ci o - 02 /0 6/ 20 14 Linguagem e Técnicas de Programação return(x); }else{ return(x + mult(x, y-1)); } } Memória RAM 101 102 103 104 105 106 107 108 109 110 2 3 2 2 2 111 112 113 114 115 116 117 118 119 120 1 Variáveis:main mult(2,3) Variáveis:mult x 101 int y 103 int 4 Variáveis:mult x 105 int y 107 int 2 Figura 487 – Retorno do valor 4 Ao receber de volta o resultado da função mult que foi chamado, a rotina soma com x e retorna, agora, 4. #include <stdio.h> void main ( ) { printf(“\n %d x %d = % d”, 2,3,mult(2,3)); } int mult(int x, int y){ if (y == 1){ return(x); }else{ return(x + mult(x, y-1)); } } 430 Re vi sã o: Ju lia na - D ia gr am aç ão : J ef er so s / M ár ci o - 02 /0 6/ 20 14 Unidade VI Memória RAM 101 102 103 104 105 106 107 108 109 110 2 3 2 2 2 111 112 113 114 115 116 117 118 119 120 1 Variáveis:main 6 Variáveis:mult x 101 Int y 103 Int 4 Figura 488 – Retorno do valor 6 Recebendo o valor de mult, a rotina retorna x + 4, que vale 6. #include <stdio.h> void main ( ) { printf(“\n %d x %d = % d”, 2,3,mult(2,3)); } Memória RAM 101 102 103 104 105 106 107 108 109 110 2 3 2 2 2 111 112 113 114 115 116 117 118 119 120 1 Variáveis:main 6 Figura 489 – Recursivamente, há o retorno do programa ao ponto em que foi chamado No exemplo da multiplicação, o ponto de parada é >. (y == 1) Regra geral: x + mult(x, y-1) 431 Re vi sã o: Ju lia na - D ia gr am aç ão : J ef er so s / M ár ci o - 02 /0 6/ 20 14 Linguagem e Técnicas de Programação observação Um dos exemplos clássicos de recursividade é a implementação do jogo Torre de Hanói em uma simples rotina: void moveHanoi(int ndiscos, char origem, char destino, char auxiliar){ if(ndiscos > 0) { moveHanoi(ndiscos -1,origem, auxiliar, destino); printf(“mover de %c para %c\n”,origem, auxiliar); moveHanoi(ndiscos -1, auxiliar, destino,origem); } } Uma vez determinada a quantidade de discos, e os pinos (‘a’, ‘b’, ‘c’) origem, destino e auxiliar, recursivamente, os pinos são alternados para serem movidos e esvaziados. lembreteO limite físico das funções recursivas é o amanho da memória RAM, pois, a cada nova chamada, a pilha das funções consome a memória livre. observação A recursividade é tão importante que alguns problemas computacionais somente podem ser resolvidos por meio dela. Programas como saída de labirinto, simulação de jogos de xadrez e simulação meteolorógica são exemplos que usam a recursividade. Saiba mais Um programa para percorrer um labirinto só pode ser feito por meio da recursividade. Cada labirinto tem um caminho diferente do outro, e, para adaptar-se a todos eles, o programa deve, repetidamente, percorrer caminhos não percorridos e voltar até o ponto em que algum caminho ainda não tenha sido percorrido, em caso de não achar saída. O uso da recursividade torna-se o ideal para esse tipo de situação. 432 Re vi sã o: Ju lia na - D ia gr am aç ão : J ef er so s / M ár ci o - 02 /0 6/ 20 14 Unidade VI A implementação está disponível no documento de autoria do professor Fernando Vanini: VANINI, F. Funções recursivas. Unicamp, 7 ago. 2008. p. 14. Apresentação de aula. Disponível em: <http://www.ic.unicamp.br/~vanini/mc202/ apresentacoes/aula3.pdf>. Acesso em: 28 maio 2014. resumo Nesta unidade, passamos a utilizar a linguagem como protótipo. A primeira coisa que fizemos foi ver como funcionam o gerenciamento e a alocação da memória. Toda variável criada possui três propriedades: o seu nome, o seu tipo e o endereço da memória em que está alocada. Utilizando as informações tipo e endereço, também podemos trabalhar no programa como se fosse uma variável. Isso é possível graças aos ponteiros, variáveis que têm a informação do endereço e do tipo que contém esse local. Graças a essa dupla atribuição, rotinas diferentes podem trabalhar com o mesmo dado, simultaneamente, assim como passar strings e vetores entre funções. Funções da biblioteca stdlib.h permitem criar novas variáveis dinamicamente, usando as funções malloc() e sizeof(), bem como fazendo um cast no tipo. Em seguida, estudamos a recursividade, na qual a função, em algum momento, volta a se chamar. Com as sucessivas chamadas, em algum instante, é preciso quebrar a cadeia de chamadas e retornar uma a uma as chamadas efetuadas, de trás para frente. Esse evento, que provoca a interrupção, fazendo a função parar de chamar-se, é denominado ponto de parada, e as mudanças efetuadas nos dados a cada chamada denominam-se regra geral. Vale lembrar que existem situações que somente é possível resolver por meio da recursividade. 433 Re vi sã o: Ju lia na - D ia gr am aç ão : J ef er so s / M ár ci o - 02 /0 6/ 20 14 Linguagem e Técnicas de Programação exercícios Questão 1. Considere as seguintes estruturas: typedef struct no{ float info; struct no* proximo; } No; typedef struct pilha{ No* primeiro; } Que tipo(s) de valor armazenará(ão) a variável “primeiro”? A) Estrutura nó. B) Endereço de memória. C) Estrutura pilha. D) Número com ponto flutuante e estrutura. E) Número com ponto flutuante e um endereço de memória. Resposta correta: alternativa B. Análse das alternativas A) Alternativa incorreta. Justificativa: entre as estruturas, aquela que armazena a estrutura de nó é no. B) Alternativa correta. Justificativa: a variável primeiro é um ponteiro, e todo ponteiro armazena um endereço de memória. C) Alternativa incorreta. Justificativa: pilha é que armazena estrutura de pilha, e não a variável “primeiro”. 434 Re vi sã o: Ju lia na - D ia gr am aç ão : J ef er so s / M ár ci o - 02 /0 6/ 20 14 Unidade VI D) Alternativa incorreta. Justificativa: não existe nenhuma estrutura que armazene ponto flutuamte e estrutura. E) Alternativa incorreta. Justificativa: a estrutura nó armazena um ponto flutuante e um endereço de memória. Questão 2. Considere o programa a seguir: #include <stdio.h> int fatorial(int num){ if(num < 0){ return(-1); } if((num == 0) || (num == 1)){ return(1); }else{ return( num * fatorial( num - 1 ) ); } } void main(){ printf(“A combinãcao de 3 2 a eh %d\n”, fatorial(3)/ (fatorial(2)* fato- rial(3-2))) ; } Considerando esse programa, assinale a alternativa correta: A) A função fatorial é recursiva, e sua regra geral é if((num == 0) || (num == 1)). B) A função fatorial é recursiva, e seu ponto de retorno é if((num == 0) || (num == 1)). C) A função fatorial não é recursiva, pois a resolução é iterativa. D) Uma função que é iterativa não pode ser recursiva. E) A função fatorial é recursiva, e seu ponto de retorno é (num * fatorial( num - 1 ). Resposta correta: alternativa B. Análise das afirmativas Justificativa geral: considerando que a função fatorial é recursiva pois chama a si, como as funções recursivas, tem duas características. 435 Re vi sã o: Ju lia na - D ia gr am aç ão : J ef er so s / M ár ci o - 02 /0 6/ 20 14 Linguagem e Técnicas de Programação O ponto de retorno é quando a recursividade é interrompida, passando a retornar uma a uma as funções chamadas. Nessa função, a única maneira de não se chamar acontece quando ocorre a condicional. if((num == 0) || (num == 1)){ return(1); A cada chamada recursiva, o retorno é modificado, conforme: ( num * fatorial( num - 1 ) Essa é a regra geral.
Compartilhar