Subalgoritmos I

Last updated: September 17th, 2018

Introdução

O que são subalgoritmos ?

  • Subalgoritmos são como pequenos programas internos à um programa. Também chamados de “funções”, por lembrarem funções matemáticas.

  • Esses subalgoritmos são definidos 1 vez no programa e podem ser usados tantas vezes quanto forem necessárias.

  • As funções ajudam a organizar um código que seja muito extenso, facilitando sua manutenção e leitura, uma vez que seria muito complicado entender um código de 1000+ linhas com tudo escrito na int main()

  • Auxiliam na modularização do código

Problemas de não usar funções

  • Repetir códigos (CTRL+C CTRL+V) diminui a legibilidade do programa;

  • Código copiado com erro precisa ser consertado em múltiplos lugares do programa;

  • Mais linhas de código do que seriam necessárias causa maior dificuldade de manutenção;

  • O programador fica perdido no código;

  • Mais difícil de fazer o debugging.

Estrutura de funções

  1. Cabeçalho - declaração da função. Inclui tipo de retorno, nome e lista de parâmetros. Como boa prática é imediatamente precedida de um comentário explicando o que a função faz.

  2. Corpo da função - Define a implementação e funcionamento da função. Idealmente não é muito extenso.

  3. Escopo da função - Marcam a abertura e o fechamento da função, “separando-a” do resto do programa.

Tipo de Função

Toda função precisa de um tipo. O tipo da função informa ao compilador se a função irá retornar alguma coisa, e qual o tipo dessa coisa, ou se a função não deve retornar nada.

O tipo da função precede o nome dela no cabeçalho. Uma função pode ser de qualquer tipo: int, float, double, etc.

Escopo

O escopo de uma função define quais linhas de código serão executadas quando a função for invocada, e também separa as variáveis locais da função do resto do programa.

Os escopos são marcados por chaves (i.e. {}). Cada chave { abre um escopo e a cada chave } fecha o último escopo aberto. Obviamente toda chave { precisa de uma chave } correspondente.

Não são apenas funções que possuem escopo, estruturas de repetição e condicionais também possuem. Por exemplo:

...
if (a > b){
    /* Escopo do if marcado por chaves. A variável local tmp só existe
    dentro do escopo */
    int tmp = a;
    a = b;
    b = tmp;
}
else {
    /* Escopo do else. A variável tmp não existe neste escopo. */
}
...
                                            

Palavra reservada return

  • A palavra return é especial, e marca o retorno de uma função. As únicas funções que podem não ter um retorno são as de tipo void.
  • O que é retornado pela função precisa ser do mesmo tipo que foi especificado no cabeçalho da função.
  • Depois que o comando return é executado a função é terminada e linhas depois dele são ignoradas.

Uma função só pode retornar um valor. Mas este valor pode ser de qualquer tipo.

Parâmetros de Funções

Algumas funções podem precisar de variáveis auxiliares enviadas de fora da função. Essas variáveis são os parâmetros. Parâmetros precisam ser definidos no cabeçalho da função, incluindo seu tipo.

Pense neles como os parâmetros de uma função matemática. Por exemplo, a função f(x) = x2 recebe um parâmetro x e calcula o quadrado dele. Em C podemos escrever a mesma função como:

int f(int x){
    return x*x;
}

Note que ambas as funções fazem a mesma coisa: retornam o quadrado do parâmetro. Para executar uma função, basta chamá-la passando o valor do parâmetro entre parênteses, por exemplo:

int a = f(5); /* a recebe 25 */
int sq_a = f(a); /* sq_a recebe 625 */
printf("%d\n", f(2)); /* Imprime 4*/
                                            

Existem algumas particularidades sobre parâmetros de função em C que serão vistos em mais detalhes na próxima aula.

Parâmetros de funções são tratados como variáveis locais e deixam de existir quando a execução da função é encerrada. Isto quer dizer que não podemos acessar parâmetros da função fora dela.

Exemplos

Funções void sem parâmetros

Esta função não possui retorno nem parâmetros.

Ela pode ser usada se for necessário mostrar as opções várias vezes.

/* Simples função para mostrar um menu. */


#ifdef _WIN32
#define CLEAR "cls"
#else
#define CLEAR "clear"
#endif

void options(){
    int i;

    /* clear screen*/
    system(CLEAR);

    /* Show options*/
    printf("Choose your option\n");
    printf("(1) First options\n");
    printf("(2) Second options\n");
    printf("(3) Third options\n");
}

int main(){
    int option;

    do{
        options();
        scanf("%d", &option);
    }while(option < 1 or option > 3);

    return 0;
}   
Download Code

Função void com parâmetros

Esta função novamente não possui retorno (pois é void) porém desta vez possui parâmetros

Possui 2 parâmetros, uma variável h e w para altura e largura, respectivamente

void printSquare(int h, int w){
    int i,j;

    /* Print first line */
    for(i = 0; i < w; i++)
        printf("#");
    printf("\n");

    /* Print left & right columns */
    for(i = 1; i < h-1; i++){
        for(j = 0; j < w; j++){
            if(!j || j == w-1)
                printf("#");
            else
                printf(" ");
        }
        printf("\n");
    }

    /* Print last line if exist */
    if(h > 1){
        for(i = 0; i < w; i++)
            printf("#");
        printf("\n");
    }

}

int main(){
    printf("What's rectangle size?\n");
    int x,y;
    scanf("%d %d", &x, &y);
    printSquare(x,y);

    return 0;

}
Download Code

Exemplo no terminal

APC$ gcc -Wall -ansi -o rectangle rectangle.c
APC$ ./rectangle 
What's rectangle size?
5 9
#########
#       #
#       #
#       #
#########

Função com retorno e parâmetro

Programa mostra quais dos 100 primeiros números são primos.

Note os returns na função.

/* Retorna 1 (true) se o numero for primo e 0 (false) caso contrário*/
int primo(int n){
    int i;
    int m = (int) sqrt(n);
    for(i = 2; i <= m; i++){
        if(n%i == 0){
            return 0;
        }
    }
    return 1;
}

int main(){
    int i;  
    for(i = 2; i <= 100; i++){
        if(primo(i) == 1)
            printf("%d eh primo\n", i);
        else
            printf("%d nao eh primo\n", i);
    }
    return 0;
}   
Download Code

Exemplo no terminal

APC$ gcc -Wall -ansi -o prime prime.c -lm
APC$ ./prime
2 eh primo
3 eh primo
4 nao eh primo
5 eh primo
6 nao eh primo
7 eh primo
8 nao eh primo
9 nao eh primo
10 nao eh primo
    .
    .
    .

Exercícios Recomendados

Materiais Extras

Variáveis Globais vs Variáveis Locais

Vimos que variáveis locais são as declaradas dentro de um escopo. As variáveis globais são aquelas cujo escopo é o programa inteiro.

Elas são declaradas fora de qualquer função (incluindo a função main)

#include <stdio.h>
int a = 10; /* variavel global */

void print(){
    printf("a = %d\n", a); /* variaveis globais podem ser acessadas em qualquer lugar do programa */
}

int main(){
    printf("a = %d\n", a);
    a += 10; /* variaveis globais podem ser alteradas em qualquer lugar do programa */
    print();
    
    return 0;
}
                        

Cuidados com variáveis globais

  • Evite usar variáveis globais indiscriminadamente. Elas devem ser usadas somente se existe algum valor que precisa realmente ser acessado por várias funções.
  • Variáveis globais diminuem a modularização do programa, tornando-o mais difícil de manutenir e gerenciar.
  • Variáveis globais tornam mais difícil debugar o programa, já que temos partes diferentes do código alterando uma mesmo variável.
  • Além disso, elas desperdiçam memória: variáveis locais só existem quando seu escopo está ativo, ou seja, só enquanto são necessárias, variáveis globais existem sempre!

Prioridade de Escopo

Se tivermos uma variável global e outra local com o mesmo nome, qual terá preferência?

Vamos ver qual escopo tem preferência através de um exemplo.

Independente da resposta, nunca é uma boa prática ter 2 variáveis com o mesmo nome!

#include <stdio.h>
/* variavel global */
int a = 0;

void question(){
    /* variavel local de question */
    int a = 50;
    printf("a: %d\n", a);
}

int main(){
    printf("global a: %d\n", a);
    /* variavel local em main */
    int a = 10;
    if(a == 10){
        printf("Variavel local tem preferencia.\n");
    }
    else{
        printf("Variavel global tem preferencia.\n");
    }
    printf("a: %d\n", a);
    printf("Agora em outro escopo - question\n");
    question();
    printf("A variavel na main foi alterada?\n");
    printf("a: %d\n", a);
    
    return 0;
}
                        
Resposta

Desafio

Agora que aprendemos sobre escopo, qual será o valor impresso em cada saída?

#include <stdio.h>
int y = 20;

int max(int a, int b){
    y += 15;
    if (a >= b) return a;
    else return b;
}

int main(){
    int x = 5;
    y = 10;
    printf("x = %d\n", x);
    printf("y = %d\n", y);
    printf("max(x,y) = %d\n", max(x,y));
    printf("y = %d\n", y);
    
    return 0;
}
                    
Resposta