Comentários

0%

Não pode faltar

Procedimentos e funções

Vanessa Cadan Scheffer

O que são funções ou procedimentos?

São blocos escritos tanto para dividir a complexidade de um problema maior quanto para evitar a repetição de códigos. Essa técnica também pode ser chamada de modularização, ou seja, um problema será resolvido em diferentes módulos (MANZANO, 2015).

Fonte: Shutterstock.

Deseja ouvir este material?

Áudio disponível no material digital.

Convite ao estudo

Caro estudante, bem-vindo à unidade sobre funções e recursividade. Ao longo do livro, você teve a oportunidade de conhecer diversas técnicas que possibilitam resolver os mais variados problemas. Esse conhecimento pode lhe abrir muitas oportunidades profissionais, uma vez que você passa a olhar e a pensar a respeito da solução de um problema de maneira mais estruturada.

Todos os computadores (e agora os smartphones e tablets) precisam de um sistema que faça o gerenciamento dos recursos, o qual é chamado de sistema operacional (SO). Existem três SO que se destacam no mercado: o MacOS, o Windows e o Linux. Segundo Counter (2018), somente o núcleo (Kernel) do código fonte do Linux apresenta mais de 20 milhões de linhas de código. Você consegue imaginar como os programadores conseguem manter tudo isso organizado? Como conseguem encontrar certas funcionalidades? Certamente não é procurando linha por linha. Tanto o desenvolvimento quanto a manutenção de um sistema só são realizáveis porque as funcionalidades são divididas em “blocos”, que são chamados de funções ou procedimentos, assunto central desta unidade de estudo, o qual o habilitará a criar soluções computacionais com tais recursos.

A carreira de desenvolvedor não se limita a criar soluções comerciais, como sistemas de vendas, sistemas de controle de estoques etc. O universo desse profissional é amplo e o que não falta são áreas com possibilidades de atuação. Você foi contratado por um laboratório de pesquisa que conta com a atuação de engenheiros, físicos, químicos e matemáticos, os quais trabalham pesquisando e desenvolvendo soluções para empresas que os contratam em regime terceirizado. Muitas vezes as soluções desenvolvidas precisam ser implementadas em um software, e você foi o escolhido para essa missão.

Nesta primeira seção você aprenderá a criar funções que retornam valores. Na segunda seção avançaremos com as funções que, além de retornar, também recebem valores. Por fim, na terceira seção, veremos uma classe especial de funções, chamadas de recursivas.

Bons estudos!

Praticar para Aprender

Você foi contratado por um laboratório de pesquisa que presta serviço terceirizado para atuar juntamente com diversos profissionais. Seu primeiro trabalho será com o núcleo de nutricionistas. Eles receberam uma demanda da construtora local para calcular o IMC (Índice de Massa Corporal) de um indivíduo e dizer qual é a sua situação. Há três tipos de situações: abaixo do peso, peso ideal e sobrepeso. A escolha de qual situação retornar depende do IMC do indivíduo, que é calculado pela fórmula IMC = Peso / Altura². A regra para escolher a situação está especificada no Quadro 3.1.

Quadro 3.1 | Regras para escolha da situação do indivíduo com base em seu IMC
Regra
Situação
IMC < 18.5 Abaixo do peso
18.5 <= IMC < 24.9 Peso ideal
IMC >= 24.9 Sobrepeso
Fonte: elaborado pela autora.

Como você poderá automatizar o processo, com base na entrada das medidas (peso e altura)? Como o programa escolherá e informará a situação do indivíduo?

Bons estudos! 

Roteiro para o professor

Até o momento, os alunos não foram apresentados ao conceito de passagem de parâmetros para uma função. Assim, eles terão duas opções: i) ler os valores das variáveis dentro da própria função (como é feito na solução proposta); ou ii) usar variáveis globais, isto é, que são declaradas fora de qualquer função, para ler o valor dessas variáveis dentro da função “main” e então manipulá-las dentro da função que calcular o IMC.

Após a resolução da situação-problema, peça aos alunos que se reúnam em grupos e destaquem possíveis desvantagens da solução elaborada por eles. Isso servirá de motivação para o assunto da próxima seção, que é justamente a passagem de parâmetros entre funções.

Você pode usar as seguintes questões para nortear a discussão com os alunos:

  • Ao declarar uma variável global, eu tenho como garantir quais funções podem alterar o seu valor e como essa alteração será feita?
  • Fazer a leitura dos dados das variáveis a partir do teclado, dentro da própria função que calcula o IMC é uma boa opção? Como eu poderia reutilizar essa função em outros contextos, por exemplo, quando os dados são lidos a partir do teclado?
  • Declarar variáveis globais ocupam mais ou menos espaço na memória do computador?
  • Se eu declarar variáveis globais para serem manipuladas pela função que calcula o IMC e essa função nunca for executada, mesmo assim as variáveis estarão ocupando espaço na memória?

conceito-chave

Em uma empresa, quando precisa resolver um problema na folha de pagamento, você se dirige até o setor financeiro. Quando vai sair de férias, precisa ir até o RH para assinar o recibo de férias. Quando precisa de um determinado suprimento, o setor responsável o atende. Mas e se não houvesse essa divisão, como você saberia a quem recorrer em determinada situação? Seria mais difícil e desorganizado, certo? Um software deve ser construído seguindo o mesmo princípio de organização: cada funcionalidade deve ser colocada em um “local” com uma respectiva identificação, para que o requisitante a possa encontrar. Uma das técnicas de programação utilizada para construir programas dessa forma é a construção de funções, que você aprenderá nesta seção.

Funções

Até esse momento, você já implementou diversos algoritmos na linguagem C. Nesses programas, mesmo sem ainda conhecer formalmente, você já utilizou funções que fazem parte das bibliotecas da linguagem C, como printf() e scanf(). Na verdade, você utilizou uma função desde a primeira implementação, mesmo sem conhecê-la, pois é uma exigência da linguagem. Observe o programa no Código 3.1 que imprime uma mensagem na tela. Veja o comando na linha 2, int main(). Esse comando especifica uma função que se chama “main” e que vai devolver para quem a requisitou um valor inteiro, nesse caso, zero. Vamos entender como construir e como funciona esse importante recurso.

Código 3.1 | Programa Hello World
#include<stdio.h>
int main(){
    printf("Hello World!");
    return 0;
}
Fonte: elaborado pela autora.

A ideia de criar programas com blocos de funcionalidades vem de uma técnica de projeto de algoritmos chamada dividir para conquistar (MANZANO; MATOS; LOURENÇO, 2015). A ideia é simples: dado um problema, este deve ser dividido em problemas menores, o que facilita a resolução e organização. A técnica consiste em três passos (Figura 3.1):

  1. Dividir: quebrar um problema em outros subproblemas menores. “Solucionar pequenos problemas, em vez de um grande problema, é, do ponto de vista computacional, supostamente mais fácil” (MANZANO; MATOS; LOURENÇO, 2015, p. 102).
  2. Conquistar: usar uma sequência de instruções separada, para resolver cada subproblema.
  3. Combinar: juntar a solução de cada subproblema para alcançar a solução completa do problema original.
Figura 3.1 | Esquema da técnica dividir para conquistar
A imagem mostra uma estrutura hierárquica, no topo está o Problema inicial, no nível debaixo estão Subproblema1, subproblema2, ... Subproblema N. Abaixo de Subproblema 1 está a Solução 1. Abaixo de Subproblema 2 está a Solução 2 e Abaixo de Subproblema N está a solução N. E abaixo de tudo, fecha-se com a Solução completa.
Fonte: adaptada de Manzano, Matos e Lourenço (2015, p. 102).

Agora que foi apresentada a ideia de subdividir um problema, podemos avançar com os conceitos sobre as funções.

Uma função é definida como um trecho de código escrito para solucionar um subproblema (SOFFNER, 2013). Esses blocos são escritos tanto para dividir a complexidade de um problema maior quanto para evitar a repetição de códigos. Essa técnica também pode ser chamada de modularização, ou seja, um problema será resolvido em diferentes módulos (MANZANO, 2015).

Assimile

A modularização é uma técnica de programação que permite a divisão da solução de um problema a fim de diminuir a complexidade, tornar o código mais organizado e evitar a repetição de códigos.

Sintaxe para criar funções

Para criar uma função utiliza-se a seguinte sintaxe:

<tipo de retorno> <nome> (<parâmetros>){
   <Comandos da função>
   <Retorno> (não obrigatório)
}

Em cada declaração da função, alguns parâmetros são obrigatórios e outros opcionais. Veja cada parâmetro.

Local da função

Em qual parte do código a função deve ser programada?

A função main(), que traduzindo significa principal, é de uso obrigatório em várias linguagens de programação, por exemplo, em C, em Java e em C#. Ela é usada para identificar qual é a rotina principal do programa e por onde a execução deve começar.

Na linguagem C, vamos adotar sempre a criação das funções (sub-rotinas) antes da função main(), por uma questão de praticidade e conveniência.

Exemplificando

Veja no Código 3.2 um programa que utiliza uma função para calcular a soma entre dois números e, a seguir, sua explicação detalhada.

Código 3.2 | Programa com a função somar()
#include<stdio.h>
int somar(){
    return 2 + 3;
}
int main(){
    int resultado = 0;
    resultado = somar();
    printf("O resultado da funcao e = %d",resultado);
    return 0;
}
Fonte: elaborado pela autora.

Teste o Código 3.2 utilizando a ferramenta Paiza.io.

A função somar() no Código 3.2 inicia-se na linha 2 e termina na linha 4, com o fechamento das chaves. Vamos analisar os parâmetros para esse caso.

<tipo de retorno> – Foi especificado que a função vai retornar um valor inteiro (int).

<nome> – somar.

<comandos da função> – Essa função foi criada de modo a retornar a soma de dois valores, tudo em um único comando: return 2 + 3.

Vale ressaltar que cada programador criará suas funções da maneira que julgar mais adequado. Por exemplo, se os comandos tivessem sido escritos na forma:

int somar(){
 int x = 0;
 x = 2 + 3;
 return x;
}

A função teria o mesmo efeito. Mas veja, da forma que está no Código 3.2, escrevemos duas linhas a menos (imagine essa diferença sendo acumulada nas mais de 20 milhões linhas do Linux). Veja que no Código 3.2 foi usada uma variável a menos, o que significa economia de memória do computador e do processamento para sua alocação.

Assimile

Os comandos que serão usados em cada função dependem da escolha de cada desenvolvedor. Tenha em mente que dado um problema, pode haver 1, 2, …, N maneiras diferentes de resolver; o importante é tentar fazer escolhas que otimizem o uso dos recursos computacionais

Outra característica da utilização de funções é que elas “quebram” a linearidade de execução, pois a execução pode “dar saltos” quando uma função é invocada (SOFFNER, 2013).

Para entender melhor como funciona esse mecanismo, vamos criar uma função que solicita um número para o usuário, calcula o quadrado desse número e retorna o resultado. Veja no Código 3.3 essa solução. Todo programa sempre começa pela função main(), ou seja, esse programa começará a ser executado na linha 10. Na linha 12 a função calcular() é chamada, ou seja, a execução “pula” para a linha 3. Na função calcular() é solicitado um valor ao usuário (linha 5), armazenado em uma variável (linha 6) e retornado o valor multiplicado por ele mesmo (linha 7). Após retornar o valor, a execução do programa “volta” para a linha 10, pois nesse ponto a função foi chamada. O resultado da função é armazenado na variável resultado e impresso na linha 13.

Código 3.3 | Função para calcular o quadrado de um número
#include<stdio.h>

float calcular() {
    float num;
    printf("\nDigite um número: ");
    scanf("%f", &num);
    return num * num;
}

int main(){
    float resultado = 0;
    resultado = calcular();
    printf("\nO quadrado do número digitado é %.2f ", resultado);
    return 0;
} 
Fonte: elaborado pela autora.

Teste o Código 3.3 utilizando a ferramenta Paiza.io.

Reflita 

A utilização de funções permite que a execução de um programa “pule” entre as linhas, ou seja, que a execução não aconteça de modo sequencial da primeira até a última linha. Essa característica pode deixar a execução mais lenta? Esses “saltos” podem, em algum momento, causar um erro de execução? Os valores das variáveis podem se perder ou se sobrepor?

O uso de funções com ponteiros na linguagem C

Você viu que uma função pode retornar um número inteiro, um real e um caractere; mas e um vetor? Será possível retornar? A resposta é sim! Para isso, devemos utilizar ponteiros (ou apontador, como alguns programadores o chamam). Não é possível criar funções como int[10] calcular(), onde int[10] quer dizer que a função retorna um vetor com 10 posições. A única forma de retornar um vetor é por meio de um ponteiro (MANZANO, 2015).

Um ponteiro é um tipo especial de variável, que armazena um endereço de memória, podemos utilizar esse recurso para retornar o endereço de um vetor; assim, a função que “chamou” terá acesso ao vetor que foi calculado dentro da função (MANZANO; MATOS; LOURENÇO, 2015).

Para exemplificar o uso desse recurso vamos implementar uma função, que cria um vetor de dez posições e os preenche com valores aleatórios, imprime os valores e posteriormente passa esse vetor para “quem” chamar a função. Veja no Código 3.4 a implementação dessa função. O programa começa sua execução pela linha 15, na função main(). Na linha 17 é criado um ponteiro do tipo inteiro, ou seja, este deverá apontar para um local que tenha número inteiro. Na linha 18 é criada uma variável para controle do laço de repetição. Na linha 20 a função gerarRandomico() é invocada; nesse momento a execução “pula” para a linha 3, a qual indica que a função retornará um endereço para valores inteiros (int*). Na linha 5 é criado um vetor de números inteiros com 10 posições e este é estático (static), ou seja, o valor desse vetor não será alterado entre diferentes chamadas dessa função. Na linha 8 é criada uma estrutura de repetição para percorrer as 10 posições do vetor. Na linha 9, para cada posição do vetor é gerado um valor aleatório por meio da função rand() % 100 (gera valores entre 0 e 99), e na linha 10 o valor gerado é impresso para que possamos comparar posteriormente. Na linha 12 a função retorna o vetor, agora preenchido, por meio do comando return r; com isso, a execução volta para a linha 20, armazenando o endereço obtido pela função no ponteiro p. Na linha 22 é criada uma estrutura de repetição para percorrer o vetor.

Código 3.4 | Função que retorna vetor
#include<stdio.h>

int* gerarRandomico() {

    static int r[10];
    int a;

    for(a = 0; a < 10; a++) {
        r[a] = rand() % 100;
        printf("\nr[%d] = %d", a, r[a]);
    }
    return r;
}

int main(){

   int *p;
   int i;

   p = gerarRandomico();

   for ( i = 0; i < 10; i++ ) {
      printf("\np[%d] = %d", i, p[i]);
   }
   return 0;
}
Fonte: elaborado pela autora.

Teste o Código 3.4 utilizando a ferramenta Paiza.io.

O resultado do Código 3.4 pode ser conferido na Figura 3.2.

Figura 3.2 | Resultado do Código 3.4
Fonte: elaborada pela autora.
Roteiro para o professor

Caro professor, uma ideia é treinar os alunos para exemplos de recursão utilizando strings. Peça para os alunos desenvolverem um código para inverter uma string utilizando recursão, com o emprego de variáveis estáticas.

Ao final, peça que os alunos analisem o código desenvolvido e o exemplo a seguir, identificando pelo menos duas vantagens de se utilizar variáveis estáticas em um código.

Uma possível fonte é http://www.br-c.org/doku.php?id=static.

No exemplo, incluímos a string “teste” na aba input. Eles podem executar o código e testar com outras palavras.

Outro uso importante de ponteiros com funções é na alocação de memória dinâmica. A função malloc(), pertencente à biblioteca <stdio.h>, é usada para alocar memória dinamicamente. Entender o tipo de retorno dessa função é muito importante, principalmente para seu avanço, quando você começar a estudar estruturas de dados. Veja esse comando:

int* memoria = (int*) malloc(sizeof(int));

A função malloc() pode retornar dois valores: NULL ou um ponteiro genérico (ponteiro genérico é do tipo void*) (MANZANO, 2015). Quando houver um problema na tentativa de alocar memória, a função retornará NULL, e quando tudo der certo, a função retornará o ponteiro genérico para a primeira posição de memória alocada (SOFFNER, 2013). Nesse caso é preciso converter esse ponteiro genérico para um tipo específico, de acordo com o tipo de dado que será guardado nesse espaço reservado. Para isso é preciso fazer uma conversão, chamada de cast, que consiste em colocar entre parênteses, antes da chamada da função, o tipo de valor para o qual se deseja converter.

O número passado entre parênteses equivale à quantidade de bytes a serem alocados pelo computador. Neste caso, o operador sizeof foi usado para calcular a quantidade de bytes necessários para se alocar um número inteiro. Usar o operador sizeof é uma boa prática, pois libera o programador da responsabilidade de determinar a quantidade de memória necessária para cada tipo de dado, na arquitetura específica em que o programa executará.

Agora que vimos o tipo de retorno da função malloc(), vamos entender como usar essa função dentro de uma função criada por nós.

Veja no Código 3.5: a função alocar() foi criada da linha 4 até 7 e seu tipo de retorno foi especificado como int*; isso significa que o espaço será alocado para guardar valores inteiros. Veja que na linha 8 foi criado um ponteiro inteiro, chamado num, e a função alocar() foi chamada, tendo seu resultado armazenado no ponteiro num. Em seguida, usamos uma estrutura condicional para saber se alocação foi feita com êxito. Caso o valor do ponteiro memoria seja diferente de NULL (linha 10), então sabemos que a alocação foi feita com sucesso, pedimos que o usuário informe um número inteiro e imprimimos seu resultado na tela.

Código 3.5 | Função para alocar memória dinamicamente
#include<stdio.h>
#include<stdlib.h>

int* alocar() {
    int *memoria = (int*) malloc(sizeof(int));
    return memoria;
}
int main(){
   int *num = alocar();
   if (num != NULL){
        printf("\nInforme um número inteiro: ");
        scanf("%d", num);
        printf("\nNúmero informado: %d", *num);
   }
   
   return 0;
}
Fonte: elaborado pela autora.

Nesta seção você aprendeu a criar funções que, após um determinado conjunto de instruções, retorna um valor para “quem” chamou a sub-rotina. Esse conhecimento permitirá a você criar programas mais organizados, além de evitar repetição de códigos.

Roteiro para o professor

Na seção 2.1 foi apresentada a função fflush(), que garante que a entrada padrão (stdin) seja limpa (sem informações de leituras anteriores) antes de fazermos a leitura de uma string, por meio da função fgets(). Apesar de seu uso ser importante, o programador pode esquecer de chamar essa função antes de realizar a leitura de uma string, por exemplo. Além disso, para cada leitura com fgets(), o programador precisará fazer também uma chamada para fflush().

Peça aos alunos que se reúnam para discutir a seguinte questão: minimizar essas limitações utilizando o conceito de funções que acabamos de aprender? Eles podem pesquisar na Web, utilizando sites como o https://pt.stackoverflow.com/ (STACK EXCHANGE INC, c2021).

Depois de um tempo, peça a eles que escrevam uma solução para o problema em linguagem C.

A ideia seria criar uma função, por exemplo lerString(), a qual executaria a função fflush() e depois fgets(). Assim, o programador poderia usar sempre a função lerString(), sem ter que se preocupar com detalhes de limpeza da entrada padrão (stdin).

Eis uma possível solução para o problema:

#include <stdio.h>
#define TAM 100

char* lerString() {  

  if (TAM <= 0) return NULL;  

  int strTam = TAM + 1; // precisa considerar o caractere de encerramento da string
  char* str = (char*) malloc(sizeof(char) * strTam);  

  fflush(stdin);
  fgets(str, strTam, stdin);  

  return str;
}

int main(){
  char* nome = lerString();  

  if (nome != NULL) printf("%s", nome);
}

Após os alunos implementarem as soluções, você pode sugerir também o teste do código apresentado, utilizando a ferramenta Paiza.io.

Faça valer a pena

Questão 1

Dado um certo problema para ser resolvido por meio de um programa, a solução pode ser implementada em blocos de funcionalidades, técnica essa conhecida como dividir para conquistar. A aplicação dessa técnica em uma linguagem de programação pode ser feita por meio de funções ou procedimentos

A respeito de funções e procedimentos, analise as afirmativas a seguir:

  1. Funções e procedimentos têm o mesmo objetivo, ou seja, resolver parte de um problema maior. Ambas as técnicas farão o processamento de uma funcionalidade e retornarão um valor para “quem” chamou a sub-rotina.
  2. Em uma função criada para retornar um valor inteiro, o comando return não pode retornar outro tipo de valor.
  3. Uma função pode ser invocada quantas vezes for necessário em um programa.

É correto o que se afirma apenas em:

Tente novamente...

Funções e procedimentos têm o mesmo objetivo, ou seja, resolver parte de um problema maior. Ambas as técnicas farão o processamento de uma funcionalidade, porém, a função retornará um valor, mas o procedimento não retorna nada (void).

Tente novamente...

Em uma função criada para retornar um valor inteiro, o comando return pode retornar outro tipo de valor, porém, o valor poderá divergir do resultado esperado.

Correto!

O objetivo é exatamente esse: que uma função seja invocada várias vezes, evitando a repetição do código da função em vários pontos do programa.

Tente novamente...

Funções e procedimentos têm o mesmo objetivo, ou seja, resolver parte de um problema maior. Ambas as técnicas farão o processamento de uma funcionalidade, porém, a função retornará um valor, mas o procedimento não retorna nada (void).

Em uma função criada para retornar um valor inteiro, o comando return pode retornar outro tipo de valor, porém, o valor poderá divergir do resultado esperado.

Tente novamente...

Em uma função criada para retornar um valor inteiro, o comando return pode retornar outro tipo de valor, porém, o valor poderá divergir do resultado esperado.

Uma função seja invocada várias vezes, evita a repetição do código da função em vários pontos do programa.

Questão 2

Funções são usadas para organizar o código, evitando a repetição de linhas de comandos. Uma boa prática de programação é avaliar se um determinado trecho precisa ser escrito mais de uma vez. Se a resposta for sim, então esse trecho deve ser transformado em uma sub-rotina.

Avalie o código a seguir.

#include<stdio.h>

int somar(){
  return 2 + 3.23;
}
int main(){
  int resultado = 0;
  resultado = somar();
  printf("\nO resultado da função é = %d", resultado);
  return 0;
}

Com base no contexto apresentado, é correto afirmar que:

Tente novamente...

Esta alternativa está incorreta, leia novamente a questão e reflita sobre o conteúdo para tentar novamente.

Tente novamente...

Esta alternativa está incorreta, leia novamente a questão e reflita sobre o conteúdo para tentar novamente.

Tente novamente...

Esta alternativa está incorreta, leia novamente a questão e reflita sobre o conteúdo para tentar novamente.

Correto!

Ao executar o programa, embora a função tenha sido programada para retornar um inteiro, um erro não será gerado, mas um valor diferente do resultado esperado será retornado. Ao somar 2 + 3.23, espera-se obter como resultado 5.23, porém, a função vai retornar somente a parte inteira do resultado, no caso, 5.‬‬‬‬‬‬‬

Tente novamente...

Esta alternativa está incorreta, leia novamente a questão e reflita sobre o conteúdo para tentar novamente.

Questão 3

Vetores são estruturas de dados estáticas, ou seja, não são redimensionadas em tempo de execução. Uma vez criadas com tamanho N, esse tamanho se mantém fixo. Para criar uma função que retorna um vetor é preciso recorrer ao uso de ponteiros.

#include<stdio.h>
#include<stdlib.h>

int* retornarVetor(){
  int* v = (int*) malloc(sizeof(int) * 10);
  int a;
  for(a = 0; a < 10; ++a) {
    v[a] = 2 * a;
  }
  return v;
}

int main(){
  int *p;
  p = retornarVetor();
  printf("\nValor = %d", p[2]);
  return 0;
}

Com base no contexto apresentado, assinale a alternativa correta.

Tente novamente...

Esta alternativa está incorreta, leia novamente a questão e reflita sobre o conteúdo para tentar novamente.

Tente novamente...

Esta alternativa está incorreta, leia novamente a questão e reflita sobre o conteúdo para tentar novamente.

Correto!

Na função retornarVetor(), o vetor é preenchido da seguinte forma:

 for(a = 0; a < 10; ++a) {
    v[a] = 2 * a;
  }

Ou seja, para cada posição a do vetor, que começa em zero e vai até 9, será guardado o dobro do valor (2 * a). Nesse caso, o vetor na memória ficará da seguinte forma:

Valor 0 2 4 6 8 10 12 14 16 18
Índice 0 1 2 3 4 5 6 7 8 9

Logo, ao executar o comando printf("Valor = %d", p[2]); será buscado o endereço inicial do vetor, representado pelo ponteiro p (ou seja, índice zero), mais duas casas, ou seja, o índice 2. Logo, o resultado nessa posição do vetor é 4.

Tente novamente...

Esta alternativa está incorreta, leia novamente a questão e reflita sobre o conteúdo para tentar novamente.

Tente novamente...

Esta alternativa está incorreta, leia novamente a questão e reflita sobre o conteúdo para tentar novamente.

Referências

[C] AULA 60 - Alocação Dinâmica - Parte 1 – Introdução. [S. l.], 5 nov. 2012. 1 vídeo (5 min. 25 s.). Publicado pelo canal Linguagem C Programação Descomplicada. Disponível em: https://goo.gl/ps7VPa. Acesso em: 30 jun. 2016.

ALMEIDA, R.; ZANLORENSSI, G. A evolução do número de linhas de telefone fixo e celular no mundo. Nexo, 2 maio 2018. Disponível em: https://cutt.ly/5jSOpbP. Acesso em: 7 jul. 2018.

COUNTER, L. Lines of code of the Linux Kernel Versions. Disponível em: https://cutt.ly/zjSOt0G. Acesso em: 30 jun. 2018.

FEOFILOFF, P. Recursão e algoritmos recursivos. Instituto de Matemática e Estatística da Universidade de São Paulo, 21 maio 2018. Disponível em: https://cutt.ly/OjSOwOH. Acesso em: 2 jan. 2018.

MANZANO, J. A. N. G. Linguagem C: acompanhada de uma xícara de café. São Paulo: Érica, 2015.

MANZANO, J. A. N. G.; MATOS, E.; LOURENÇO, A. E. Algoritmos: técnicas de programação. São Paulo: Érica, 2015.

OLIVEIRA, R. Algoritmos e programação de computadores. Notas de aula. Instituto de Computação da Universidade Estadual de Campinas – UNICAMP, [s. d.]. Disponível em: https://cutt.ly/mjSI75P. Acesso em: 16 jul. 2018.

PIRES, A. A. Cálculo numérico: prática com algoritmos e planilhas. São Paulo: Atlas, 2015.

SOFFNER, R. Algoritmos e programação em linguagem C. São Paulo: Saraiva, 2013.

STACK EXCHANGE INC. Problema no FGTES no C [duplicada]. Stack Overflow, c2021. Disponível em: https://cutt.ly/PjSI38E. Acesso em: 10 jan. 2021.

Bons estudos!

AVALIE ESTE MATERIAL

OBRIGADO PELO SEU FEEDBACK!