Declaração, definição, chamada e passagem de parâmetros por valor e por referência.
Objectivos
Ao final desta aula, o estudante deverá ser capaz de:
Estrutura
Introdução
Uma função é um bloco de código com um nome, que executa uma tarefa específica e pode ser chamada em qualquer parte do programa.
Em C, todo programa é uma colecção de funções. A execução começa sempre pela função main().
Escreve-se o código uma vez e usa-se em vários locais.
Divide-se o problema em partes mais pequenas e gerenciáveis.
Permite testar cada função isoladamente antes de integrar.
Um bom nome de função documenta a intenção do código.
Corrigir um erro num único lugar afecta todos os chamadores.
Evita duplicação de código, reduzindo o tamanho do binário.
Anatomia
tipoRetorno nomeDaFuncao(tipo1 param1, tipo2 param2) {
// corpo da função
return valor;
}
| Elemento | Descrição |
|---|---|
tipoRetorno | Tipo de valor devolvido (int, float, char, void…). |
nomeDaFuncao | Identificador único (letra/underscore inicial). |
param1, param2 | Lista de parâmetros (pode ser vazia ou void). |
{ ... } | Corpo com as instruções a executar. |
return | Devolve o valor ao ponto de chamada e termina a função. |
void significa "nada". Usamos void como retorno quando a função não devolve valor, e (void) como parâmetro quando a função não recebe nada.
Declaração
O protótipo informa o compilador sobre a existência da função, o seu nome, tipo de retorno e parâmetros — antes de a função ser usada.
// Protótipo (termina com ponto-e-vírgula)
int soma(int a, int b);
void ligaLed(int numero);
float media(float x, float y);
.c ou num ficheiro .h. Isto resolve o problema de referência circular.#include <stdio.h>
int soma(int a, int b); // protótipo
int main(void) {
printf("%d\n", soma(3, 4));
return 0;
}
int soma(int a, int b) {
return a + b;
}
Definição
A definição contém o corpo com os comandos que a função executa quando for chamada.
int soma(int a, int b) {
int resultado;
resultado = a + b;
return resultado;
}
void desenhaLinha(void) {
int i;
for (i = 0; i < 10; i++) {
printf("*");
}
printf("\n");
}
return devolve o valor e termina a função.return.void, pode-se omitir o return.return numa função não-void.}.
Chamada
Para executar uma função, escreve-se o seu nome seguido de parênteses com os argumentos. O fluxo salta para a função, executa-a e volta ao ponto de chamada.
int main(void) {
int r;
r = soma(3, 4); // chamada → r = 7
desenhaLinha(); // chamada sem retorno
return 0;
}
// Chamadas aninhadas — a função pode chamar outras funções
float mediaFinal(float a, float b) {
return media(a, b) * 1.1;
}
Parâmetros
Quando uma função é chamada, os valores passados são copiados para os parâmetros formais declarados na função. Este é o mecanismo padrão da linguagem C: passagem por valor.
Declarados na definição da função. São variáveis locais que existem apenas dentro da função.
int soma(int a, int b)
↑ ↑
formais
Valores ou variáveis passados na chamada. As suas cópias é que chegam à função.
soma(3, x);
↑ ↑
reais
Por Valor
A função recebe uma cópia do valor do argumento. Alterações feitas dentro da função não afectam a variável original.
void duplica(int n) {
n = n * 2;
printf("Dentro: n = %d\n", n);
}
int main(void) {
int x = 10;
duplica(x);
printf("Fora: x = %d\n", x); // 10 (não mudou!)
return 0;
}
Por Referência
Em vez de passar o valor, passamos o endereço da variável (com &). Dentro da função usamos um ponteiro para aceder e modificar o valor original (com *).
void duplica(int *n) {
*n = (*n) * 2;
}
int main(void) {
int x = 10;
duplica(&x);
printf("x = %d\n", x); // 20
return 0;
}
n é passado por valor (copia-se o endereço), mas como esse endereço aponta para x, alterar *n altera x.
Comparação
| Aspecto | Por Valor | Por Referência (ponteiros) |
|---|---|---|
| Sintaxe na chamada | f(x) | f(&x) |
| Parâmetro formal | int x | int *x |
| O que é copiado | O valor da variável | O endereço da variável |
| Alterações dentro da função | Não afectam o original | Afectam o original |
| Consumo de memória | Alto (cópias grandes) | Baixo (só o endereço) |
| Segurança | Alta (isolamento) | Requer cuidado |
| Uso típico | Valores pequenos | Structs, arrays, retorno múltiplo |
Exemplo
Em C, uma função devolve um único valor com return. Para devolver vários, usamos ponteiros como parâmetros de saída.
void calc(int a, int b, int *soma, int *prod) {
*soma = a + b;
*prod = a * b;
}
int main(void) {
int s, p;
calc(3, 4, &s, &p);
printf("Soma = %d, Produto = %d\n", s, p);
return 0;
}
void troca(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
int x = 5, y = 9;
troca(&x, &y); // x = 9, y = 5
Escopo
int contadorGlobal;
void incrementa(void) {
static int contadorLocal = 0;
contadorLocal++;
contadorGlobal++;
printf("Local: %d, Global: %d\n", contadorLocal, contadorGlobal);
}
static faz com que uma variável local mantenha o seu valor entre chamadas à função, sem se tornar global.
Aplicação 1
Uma biblioteca é um conjunto de funções com uma funcionalidade comum, organizada em dois ficheiros: .h (cabeçalho) e .c (implementação).
#ifndef MATH_H
#define MATH_H
int quadrado(int x);
int fatorial(int n);
float grausParaRad(float g);
void raizes(float a, float b,
float c, float *x1, float *x2);
#endif
#include "math.h"
int quadrado(int x) {
return x * x;
}
float grausParaRad(float g) {
return g * 2 * 3.14159 / 360;
}
#include "math.h"
int main(void) {
printf("%d\n", quadrado(5)); // 25
return 0;
}
#ifndef MATH_H evita referências circulares e inclusões repetidas do mesmo header.
Aplicação 2
Nos sistemas embarcados, as funções são usadas para abstrair o acesso ao hardware e criar camadas de software reutilizáveis.
// led.h
void ledInit(int pino);
void ledOn(int pino);
void ledOff(int pino);
void ledToggle(int pino);
// led.c
void ledOn(int pino) {
digitalWrite(pino, HIGH);
}
void lerSensorFiltrado(int *valorFiltrado, int nAmostras) {
long soma = 0;
int i;
for (i = 0; i < nAmostras; i++) {
soma += adRead(0);
}
*valorFiltrado = soma / nAmostras;
}
Erros Comuns
int main(void) {
soma(3, 4); // erro se soma()
// ainda não foi vista
}
void f(int *p) { *p = 5; }
int x;
f(x); // ❌ faltou o &
f(&x); // ✅
int soma(int a, int b) {
a + b; // ❌ sem return
}
void duplica(int n) {
n = n * 2; // só altera a cópia
}
Prática
Crie soma e produto que recebam dois inteiros e devolvam o resultado.
Crie int isPar(int n) que retorne 1 se n é par.
Implemente void troca(int *a, int *b) usando ponteiros.
Crie void minMax(int v[], int n, int *min, int *max).
Crie void raizes(float a, float b, float c, float *x1, float *x2).
Crie conv.h/conv.c com funções: celsiusParaFahrenheit, kmParaMilhas, kgParaLibras.
Implemente ledInit, ledOn, ledOff, ledToggle.
Implemente void mediaMovel(int *v, int n, int tam, float *resultado).
Resumo
&).*)..h + .c).
Referências
ASCENCIO, Ana Fernanda Gomes; VENERUCH, Edilene Aparecida.
Fundamentos da Programação de Computadores: algoritmos, PASCAL, C/C++ (padrão ANSI) e JAVA.
3. ed. Pearson Editora, 2014.
ALMEIDA, Rodrigo Maximiano Antunes de; MORAES, Carlos Henrique Valério; SERAPHIM, Thatyana de Faria Piola.
Programação de Sistemas Embarcados: Desenvolvendo Software para Microcontroladores em Linguagem C.
Elsevier, 2016. (Capítulo 9)
Material didático do ISPSongo — Plano Analítico de Introdução à Programação em C.
Obrigado! Bons estudos e boa programação em C.