Funções


Funções em C são blocos de código que realizam tarefas específicas e podem ser chamados de outros lugares do programa. Elas desempenham um papel fundamental na modularização e organização do código, permitindo dividir um programa complexo em partes menores e mais gerenciáveis. Funções melhoram a reutilização do código, a legibilidade e a manutenção, além de permitirem a criação de programas mais estruturados.

Vantagens

  1. Reutilização de código: Uma função pode ser definida uma vez e chamada várias vezes de diferentes partes do programa, evitando duplicação de código. As bibliotecas são um bom exemplo na reutilização de código. A biblioteca math disponibiliza funções prontos como cos, sin, pow, entre outras.

  2. Modularização: Dividir um programa em funções ajuda a estruturar o código em partes menores e independentes, tornando o desenvolvimento e a manutenção mais fáceis.

  3. Legibilidade: Funções permitem nomear blocos de código com significados claros, tornando o código mais fácil de entender.

  4. Depuração: Funções isoladas são mais fáceis de testar e depurar, já que você pode focar em partes específicas do código.

Sintaxe

A sintaxe básica para definir e chamar funções em C é a seguinte:

// Declaração da função
tipo_de_retorno nome_da_funcao(parametros) {
    // Corpo da função
    // ...
    return valor_de_retorno;
}
 
int main() {
    // Chamada da função
    tipo_de_retorno resultado = nome_da_funcao(argumentos);
    // ...
    return 0;
}
  • tipo_de_retorno: Indica o tipo de dado que a função retornará.

  • nome_da_funcao: O nome único dado à função.

  • parametros: Parâmetros de entrada da função.

  • valor_de_retorno: O valor que a função retorna.

Exemplo

#include <stdio.h>
 
// Definição da função
int soma(int a, int b) {
    int resultado = a + b;
    return resultado;
}
 
int main() {
    int x = 5, y = 3;
 
    // Chamada da função
    int resultado_soma = soma(x, y);
 
    printf("A soma de %d e %d é %d\n", x, y, resultado_soma);
 
    return 0;
}

Neste exemplo, a função soma recebe dois números inteiros como entrada, realiza a soma e retorna o resultado.

Parâmetros e retorno

As funções podem receber parâmetros (entradas) e retornar valores (saídas). Parâmetros são valores passados para a função quando ela é chamada, permitindo que a função trabalhe com dados específicos. O valor de retorno é o valor que a função entrega de volta ao ponto de chamada.

int quadrado(int num) {
    return num * num;
}
 
float divisao(float a, float b) {
    if (b == 0) {
        printf("Erro: divisão por zero\n");
        return -1; // Valor de erro
    }
    return a / b;
}

No exemplo acima, a função quadrado recebe um número e retorna o seu quadrado. A função divisao recebe dois números e retorna o resultado da divisão, verificando antes se o divisor é zero.

É possível ter mais de uma instrução return dentro de uma função, lembrando que uma função é encerrada quando executar um return ou quando chegar no fim do corpo da função. Caso um função tenha como retorno void, ainda é possível executar o return e esse comportamento é chamado de early exit (saída antecipada).

A seguir um exemplo de saída antecipada:

#include <stdio.h>
 
void exibir_impar(int n)
{
    if (n % 2 == 0) return; // early exit
 
    printf("%d é ímpar.\n", n);
}
 
// ...

Existe uma diferença sutil entre uma função sem parâmetros e uma função com parâmetros vazios (void). A seguir a explicação sobre a diferença entre essas duas declarações:

  1. Função sem parâmetros:

    Uma função declarada sem parâmetros pode receber qualquer número de argumentos ao ser chamada. Isso significa que você pode chamá-la sem passar nenhum argumento, ou você pode passar argumentos, mas eles serão ignorados. A função ainda pode acessar variáveis globais e outras informações fora dela.

    void fn1() {
        // Código da função
    }
  2. Função com parâmetros vazios void:

    Uma função declarada com parâmetros vazios é explicitamente indicada como não aceitando nenhum argumento. Se você tentar passar argumentos para essa função ao chamá-la, o compilador emitirá um aviso ou erro. Essa declaração é uma maneira de comunicar claramente que a função não espera receber nenhum parâmetro.

    void fn2(void) {
        // Código da função
    }

Protótipo de função

Um protótipo de função em C é uma declaração antecipada da função antes de sua definição completa. Ele informa ao compilador sobre a existência (assinatura) da função, seu nome, tipo de retorno e os tipos de parâmetros que ela espera receber, lembrado que o protótipo/assinatura não requer alocação de memória. A inclusão de protótipos de função no código é uma prática recomendada, pois permite ao compilador verificar se as chamadas de função estão corretas e se os tipos de parâmetros correspondem, antes mesmo de chegar à definição da função.

Os protótipos de função são particularmente úteis em cenários em que uma função é definida depois de ser chamada ou quando várias funções se chamam de forma recursiva. Além disso, em grandes projetos, eles ajudam a manter uma organização clara e evitam erros de compilação relacionados a chamadas de função incorretas.

A seguir um exemplo de protótipo de função:

// Protótipo de função
int calcularSoma(int a, int b);
 
int main() {
    int x = 5, y = 3;
    int resultado = calcularSoma(x, y);
    // ...
    return 0;
}
 
// Definição de função
int calcularSoma(int a, int b) {
    return a + b;
}

Nesse exemplo, o protótipo da função calcularSoma é declarado antes da função main(). Isso permite que a função main() chame calcularSoma antes de sua definição. Quando o compilador encontra a chamada de função, ele já sabe o tipo de retorno e os tipos de parâmetros esperados pela função, evitando erros de compilação.

Os protótipos de função podem ser declarados em várias partes do código: no início do arquivo, antes da função main() ou antes de qualquer outra função que os utilize. Além disso, eles podem ser agrupados em arquivos de cabeçalho separados, que são incluídos no código principal usando a diretiva #include.

Há duas maneiras de usar protótipos de função: protótipos internos e protótipos externos.

Protótipos internos

Os protótipos internos são colocados dentro do arquivo que contém a função main() e têm a mesma assinatura da função real. Eles agem como uma “promessa” de que a função será definida em algum lugar no código.

#include <stdio.h>
 
int calcularSoma(int a, int b); // Protótipo interno
 
int main() {
    int x = 5, y = 3;
    int resultado = calcularSoma(x, y);
    // ...
    return 0;
}
 
int calcularSoma(int a, int b) { // Definição da função
    return a + b;
}

Protótipos Externos

Os protótipos externos são definidos em um arquivo de cabeçalho separado (geralmente com extensão .h) e incluídos no código usando #include. Essa é uma abordagem comum para projetos maiores, onde as definições de função estão separadas das implementações.

// arquivo: calcular.h
#ifndef CALCULAR_H
#define CALCULAR_H
 
int calcularSoma(int a, int b);
 
#endif
// arquivo: main.c
#include <stdio.h>
#include "calcular.h" // Incluindo o arquivo de cabeçalho
 
int main() {
    int x = 5, y = 3;
    int resultado = calcularSoma(x, y);
    // ...
    return 0;
}
 
// arquivo: calcular.c
#include "calcular.h"
 
int calcularSoma(int a, int b) {
    return a + b;
}

Neste exemplo, o arquivo de cabeçalho calcular.h contém o protótipo da função calcularSoma. O arquivo main.c inclui esse arquivo de cabeçalho e, portanto, pode chamar a função sem a definição completa. A definição real da função está no arquivo calcular.c.

Em resumo, os protótipos de função em C são declarações antecipadas de funções que informam ao compilador sobre a existência, tipo de retorno e tipos de parâmetros da função. Eles ajudam a evitar erros de compilação, melhoram a organização do código e são usados tanto em protótipos internos como em protótipos externos, dependendo das necessidades do projeto.

Variáveis locais e globais

Variáveis declaradas dentro de uma função são chamadas de variáveis locais. Elas são visíveis apenas dentro da função e não podem ser acessadas fora dela. Variáveis declaradas fora de qualquer função, no escopo global, são chamadas de variáveis globais. Elas podem ser acessadas e modificadas de qualquer parte do programa, mas podem dificultar o rastreamento e a depuração do código.

Vale ressaltar que a alteração dos parâmetros de uma função não interfere no valor original.

Função main()

A função main() é o ponto de entrada para um programa em C e é obrigatória em todos os programas C. Ela é chamada automaticamente quando o programa é executado e é o ponto de partida para a execução do código. A assinatura da função main() pode variar para permitir que o programa receba argumentos da linha de comando.

Aqui estão as variações de assinatura da função main():

  1. int main()

    A assinatura mais básica da função main() é sem argumentos. Isso significa que o programa não receberá nenhum argumento da linha de comando. O código dentro da função main() será executado, mas o programa não terá acesso aos argumentos fornecidos durante a execução.

    int main() {
        // Código do programa
        return 0;
    }
  2. int main(int argc, char* argv[])

    A segunda variação da assinatura da função main() inclui dois parâmetros: argc e argv. Esses parâmetros são usados para receber argumentos da linha de comando.

    • argc (argument count): É o número de argumentos passados para o programa, incluindo o nome do próprio programa. Portanto, argc é sempre pelo menos 1.

    • argv (argument vector): É um vetor de strings que contém os argumentos passados para o programa. argv[0] é o nome do programa em si, e os argumentos subsequentes estão em argv[1], argv[2], e assim por diante.

    int main(int argc, char *argv[]) {
        // Código do programa que utiliza argc e argv
        return 0;
    }
  3. **int main(int argc, char **argv)**

    A terceira variação da função main() usa a notação char **argv, que significa um “ponteiro para um ponteiro para char”. Isso permite que você crie um array de strings, em que cada elemento do array é um ponteiro para uma string. Cada string é um argumento passado da linha de comando.

    int main(int argc, char **argv) {
        // Código do programa que utiliza argc e argv
        return 0;
    }

Geralmente, a segunda e a terceira variação da função main() são usadas quando o programa precisa receber argumentos da linha de comando, como configurações, opções ou nomes de arquivos. A variação sem argumentos (int main()) é usada quando o programa não requer entrada da linha de comando.

Referências