void, com retorno, com parâmetros e com ponteiros — quando usar cada uma
Na Aula 13 você aprendeu a estrutura básica de uma função: como declará-la, defini-la e chamá-la. Agora vamos entender as quatro categorias que existem em C, cada uma adequada para um tipo diferente de situação.
| # | Categoria | Retorna algo? | Recebe dados? | Quando usar |
|---|---|---|---|---|
| I | Função void |
❌ Não | ❌ Não | Ação pura: imprimir, separador, menu |
| II | Com valor de retorno | ✅ Sim | ❌ Não (ou usa globais) | Cálculo que produz um resultado único |
| III | Com parâmetros | ✅ Sim (ou void) | ✅ Sim — cópias dos valores | Cálculo com dados fornecidos pelo chamador |
| IV | Com ponteiros | void (resultado via ponteiro) | ✅ Sim — endereços de memória | Modificar variáveis originais ou retornar múltiplos resultados |
voidUma função void realiza uma ação mas não devolve nenhum resultado. Ela é chamada por seus efeitos — imprimir algo na tela, modificar uma estrutura, tocar um alarme — e não pelo valor que produz (porque não produz nenhum).
void têm void como tipo de retorno. Não possuem instrução return valor — apenas return; vazio (opcional) para sair antecipadamente, ou simplesmente fecham com }.
#include <stdio.h>
void imprimirSeparador() {
printf("================================\n");
}
void imprimirCabecalho() {
imprimirSeparador();
printf(" SISTEMA DE NOTAS\n");
imprimirSeparador();
}
int main() {
imprimirCabecalho();
printf(" Aluno: Ana Souza\n");
imprimirSeparador();
return 0;
}
Uma função void pode receber parâmetros — ela só não retorna nada.
#include <stdio.h>
void exibirSituacao(char *nome, float media) {
printf("%-20s | Media: %.1f | %s\n",
nome,
media,
media >= 7.0f ? "Aprovado" : "Reprovado");
}
int main() {
exibirSituacao("Carlos Mota", 8.5f);
exibirSituacao("Beatriz Lima", 5.0f);
exibirSituacao("Diego Farias", 7.0f);
return 0;
}
void: Sempre que o propósito da função for um efeito (imprimir, gravar, atualizar tela) e não um cálculo com resultado. Separar a apresentação dos dados da lógica de cálculo é uma das bases da modularização.
Funções com valor de retorno calculam um resultado e o devolvem ao chamador com a instrução return. O chamador pode usar esse valor em expressões, atribuições ou diretamente em outros printf.
return expressão; termina a execução da função e entrega o valor ao ponto da chamada. Uma função pode ter múltiplos return em caminhos diferentes — mas sempre retorna um único valor.
#include <stdio.h>
int sucessor(int n) {
return n + 1;
}
int predecessor(int n) {
return n - 1;
}
int main() {
int a = 10;
printf("Número: %d\n", a);
printf("Sucessor: %d\n", sucessor(a));
printf("Predecessor: %d\n", predecessor(a));
return 0;
}
return e lógica de decisão
#include <stdio.h>
char classificarNota(float nota) {
if (nota >= 9.0f) return 'A';
if (nota >= 7.0f) return 'B';
if (nota >= 5.0f) return 'C';
return 'D'; // abaixo de 5
}
int ehPrimo(int n) {
if (n < 2) return 0;
for (int i = 2; i * i <= n; i++)
if (n % i == 0) return 0;
return 1; // 1 = verdadeiro em C
}
int main() {
printf("Nota 8.5 -> conceito %c\n", classificarNota(8.5f));
printf("Nota 5.0 -> conceito %c\n", classificarNota(5.0f));
printf("Nota 3.0 -> conceito %c\n", classificarNota(3.0f));
for (int i = 1; i <= 15; i++)
if (ehPrimo(i))
printf("%d ", i);
printf("\n");
return 0;
}
#include <stdio.h> /* Exibe o resultado — void, efeito colateral */ void mostrarSoma(int a, int b) { printf("A soma de %d e %d é %d\n", a, b, a + b); } /* Retorna o resultado — reutilizável em expressões */ int calcularSoma(int a, int b) { return a + b; } int main() { mostrarSoma(4, 7); int resultado = calcularSoma(4, 7); printf("Resultado guardado: %d\n", resultado); /* A versão com retorno pode ser usada diretamente em expressões: */ printf("Dobro da soma: %d\n", calcularSoma(4, 7) * 2); return 0; }
void. Prefira retorno: funções com retorno são mais fáceis de testar e reutilizar.
Parâmetros são a forma de alimentar a função com dados externos. Sem parâmetros, uma função só pode trabalhar com constantes ou variáveis globais — o que a torna pouco reutilizável. Com parâmetros, a mesma função serve para qualquer conjunto de dados.
float calcularArea(float base, float altura). Ao chamar a função, o chamador fornece os argumentos — os valores concretos. A função recebe cópias desses valores e trabalha com elas internamente.
#include <stdio.h>
#define PI 3.14159f
float areaRetangulo(float base, float altura) {
return base * altura;
}
float areaTriangulo(float base, float altura) {
return (base * altura) / 2.0f;
}
float areaCirculo(float raio) {
return PI * raio * raio;
}
int main() {
printf("Retangulo 5x3: %.2f\n", areaRetangulo(5.0f, 3.0f));
printf("Triangulo 6x4: %.2f\n", areaTriangulo(6.0f, 4.0f));
printf("Circulo r=7: %.2f\n", areaCirculo(7.0f));
return 0;
}
#include <stdio.h>
float calcularSalario(float horasTrabalhadas, float valorHora, float bonus) {
float bruto = horasTrabalhadas * valorHora + bonus;
return bruto;
}
void imprimirHolerite(char *nome, float horas, float valorHora, float bonus) {
float salario = calcularSalario(horas, valorHora, bonus);
printf("Funcionario: %-15s | Horas: %.0f | Salario: R$%.2f\n",
nome, horas, salario);
}
int main() {
imprimirHolerite("Ana Oliveira", 160.0f, 25.50f, 300.0f);
imprimirHolerite("Bruno Santos", 120.0f, 30.00f, 0.0f);
imprimirHolerite("Carla Mendes", 176.0f, 22.00f, 500.0f);
return 0;
}
Uma função pode chamar outras funções, passando seus parâmetros adiante ou usando seus retornos como argumentos:
#include <stdio.h>
#include <math.h>
float calcularHipotenusa(float cateto1, float cateto2) {
return sqrt(cateto1 * cateto1 + cateto2 * cateto2);
}
int classificarTriangulo(float a, float b, float c) {
/* retorna: 1=acutângulo 2=retângulo 3=obtusângulo 0=inválido */
if (a <= 0 || b <= 0 || c <= 0) return 0;
if (a+b <= c || a+c <= b || b+c <= a) return 0;
float maior2 = (a>b && a>c) ? a*a : (b>c ? b*b : c*c);
float soma = a*a + b*b + c*c - maior2;
if (maior2 < soma) return 1;
if (maior2 == soma) return 2;
return 3;
}
int main() {
float hip = calcularHipotenusa(3.0f, 4.0f);
printf("Hipotenusa de (3, 4): %.2f\n", hip);
const char *tipos[] = {"Invalido","Acutangulo","Retangulo","Obtusangulo"};
printf("Triangulo 3-4-5: %s\n", tipos[classificarTriangulo(3,4,5)]);
printf("Triangulo 5-5-5: %s\n", tipos[classificarTriangulo(5,5,5)]);
printf("Triangulo 2-2-9: %s\n", tipos[classificarTriangulo(2,2,9)]);
return 0;
}
Parâmetros normais são cópias — a função não pode alterar a variável original. Às vezes isso é exatamente o que precisamos. Outras vezes, precisamos que a função modifique a variável original ou devolva mais de um resultado. Para isso, usamos ponteiros como parâmetros.
&x), a função pode ler e modificar diretamente o valor original, sem precisar de return para isso.
#include <stdio.h> /* Sem ponteiro: modifica apenas a cópia — original intocado */ void tentarIncrementar(int n) { n++; // afeta só a cópia local } /* Com ponteiro: acessa e modifica o original */ void incrementar(int *n) { (*n)++; // modifica no endereço apontado } int main() { int pontos = 10; tentarIncrementar(pontos); printf("Após tentativa: %d\n", pontos); // ainda 10 incrementar(&pontos); printf("Após ponteiro: %d\n", pontos); // agora 11 return 0; }
Uma função só pode ter um return. Com ponteiros, podemos "retornar" vários resultados escrevendo diretamente nos endereços fornecidos pelo chamador:
#include <stdio.h> /* Calcula mínimo e máximo de um vetor — dois resultados via ponteiro */ void minMax(int v[], int tam, int *minimo, int *maximo) { *minimo = *maximo = v[0]; for (int i = 1; i < tam; i++) { if (v[i] < *minimo) *minimo = v[i]; if (v[i] > *maximo) *maximo = v[i]; } } /* Calcula média e desvio (simplificado) — dois resultados via ponteiro */ void estatisticas(float v[], int tam, float *media, float *amplitude) { float soma = 0, mn = v[0], mx = v[0]; for (int i = 0; i < tam; i++) { soma += v[i]; if (v[i] < mn) mn = v[i]; if (v[i] > mx) mx = v[i]; } *media = soma / tam; *amplitude = mx - mn; } int main() { int notas[] = {7, 3, 9, 5, 8, 2, 10}; int mn, mx; minMax(notas, 7, &mn, &mx); printf("Notas — min: %d | max: %d\n", mn, mx); float precos[] = {12.5f, 8.0f, 25.0f, 5.5f, 18.0f}; float med, amp; estatisticas(precos, 5, &med, &); printf("Precos — media: %.1f | amplitude: %.1f\n", med, amp); return 0; }
#include <stdio.h>
void exibir(const int *v, int tam) { // const: só leitura
for (int i = 0; i < tam; i++)
printf("%d ", v[i]);
printf("\n");
}
void aplicarDesconto(int *precos, int tam, int percentual) {
for (int i = 0; i < tam; i++)
precos[i] -= precos[i] * percentual / 100;
}
int main() {
int tabela[] = {100, 250, 80, 320, 150};
printf("Antes: "); exibir(tabela, 5);
aplicarDesconto(tabela, 5, 10); // desconto de 10%
printf("Depois: "); exibir(tabela, 5);
return 0;
}
tabela para uma função, C passa automaticamente o endereço do primeiro elemento — por isso aplicarDesconto modifica o array original sem precisar de & na chamada. Use const quando a função só lê o array, para deixar a intenção clara e o compilador vigilante.
Um programa de boletim escolar que usa as quatro categorias em conjunto:
#include <stdio.h> /* CATEGORIA I — void sem retorno */ void imprimirLinha() { printf("------------------------------------------\n"); } /* CATEGORIA II — com retorno */ char conceito(float media) { if (media >= 9.0f) return 'A'; if (media >= 7.0f) return 'B'; if (media >= 5.0f) return 'C'; return 'D'; } /* CATEGORIA III — com parâmetros por valor */ float calcularMedia(float n1, float n2, float n3) { return (n1 + n2 + n3) / 3.0f; } /* CATEGORIA IV — com ponteiros: calcula melhor e pior média */ void extremosTurma(float medias[], int tam, float *melhor, float *pior) { *melhor = *pior = medias[0]; for (int i = 1; i < tam; i++) { if (medias[i] > *melhor) *melhor = medias[i]; if (medias[i] < *pior) *pior = medias[i]; } } int main() { const char *nomes[] = {"Ana", "Bruno", "Carla", "Diego"}; float notas[][3] = { {8.0f, 9.0f, 7.5f}, {5.0f, 6.0f, 4.5f}, {10.0f, 9.5f, 9.0f}, {6.5f, 7.0f, 6.0f} }; float medias[4]; int n = 4; imprimirLinha(); printf("%-10s %5s %5s %5s %6s %s\n", "Nome","N1","N2","N3","Media","Conceito"); imprimirLinha(); for (int i = 0; i < n; i++) { medias[i] = calcularMedia(notas[i][0], notas[i][1], notas[i][2]); printf("%-10s %5.1f %5.1f %5.1f %6.1f %c\n", nomes[i], notas[i][0], notas[i][1], notas[i][2], medias[i], conceito(medias[i])); } imprimirLinha(); float melhor, pior; extremosTurma(medias, n, &melhor, &pior); printf("Melhor media: %.1f | Pior media: %.1f\n", melhor, pior); imprimirLinha(); return 0; }