Menu

Funções em C: declarar, chamar e retornar valores

Como definir e chamar funções em C - o tipo de retorno, os parâmetros, os valores de retorno, funções void, por que a decomposição importa, e um exemplo completo montado com pequenas peças nomeadas.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

Por que funções

Tudo até aqui viveu dentro da main. Isso funciona até um programa fazer mais de uma coisa, e então três problemas chegam de uma vez: o mesmo bloco de código é copiado para três lugares, a main cresce além do que alguém consegue manter na cabeça, e não há como dar nome ao propósito de um trecho de código.

Uma função resolve os três. Ela dá um nome a um bloco de código, suas próprias variáveis locais, uma lista de entradas e um único valor para devolver.

square é escrita uma vez e chamada três vezes, incluindo uma vez sobre o próprio resultado. Você já vinha chamando funções - printf é uma, e a main também.

Anatomia de uma função

returnType name(parameterList) {
    // corpo
    return value;
}

Quatro partes:

  • Tipo de retorno - o tipo do valor que a função devolve. int, double, char, um tipo ponteiro, ou void para "nada".
  • Nome - segue as mesmas regras de um nome de variável, e deve dizer o que a função faz ou produz.
  • Lista de parâmetros - as entradas, cada uma com seu próprio tipo, separadas por vírgulas. (void) significa que ela não recebe nenhuma.
  • Corpo - as instruções, entre chaves, terminando com um return a menos que o tipo de retorno seja void.

Repare que pi() ainda precisa dos parênteses vazios no ponto da chamada - é isso que faz dela uma chamada, e não apenas uma menção ao nome. E printBanner(20); é uma instrução sozinha, porque não produz valor algum para usar.

Escreva (void), não (), para uma função sem parâmetros. Em C essas duas coisas significam coisas diferentes, e () é a forma frouxa - falaremos mais disso em instantes.

Chamar e retornar

Uma chamada transfere o controle para dentro da função, executa seu corpo e volta com um valor que substitui a chamada na expressão ao redor.

return faz duas coisas ao mesmo tempo: fornece o valor e encerra a função na hora. Qualquer coisa depois de um return no mesmo caminho nunca executa, e é isso que faz o estilo de retorno antecipado de larger funcionar - nenhum else é necessário, porque chegar à última linha já significa que a > b era falso.

Uma função pode ter vários comandos return. Ela só consegue executar um deles por chamada.

Funções void

Uma função que existe pelo seu efeito em vez de pelo seu valor tem o tipo de retorno void: imprimir, atualizar algo por meio de um ponteiro, desenhar.

Dentro de uma função void, return; sem valor sai mais cedo. return algumValor; ali é um erro de compilação, e usar a chamada em uma expressão também - int x = printTable(7, 5); não compila, o que é o sistema de tipos fazendo seu trabalho.

Decomposição: a verdadeira razão

Funções não são principalmente sobre evitar repetição. Elas são sobre transformar um procedimento longo em alguns passos nomeados, de modo que a main se leia como uma descrição do programa em vez de sua implementação.

A main agora são quatro linhas de saída e um cálculo. Cada auxiliar faz uma coisa, tem um nome que diz qual é essa coisa, e pode ser testada sozinha. Repare que o comprimento do array viaja como um segundo parâmetro: uma vez que um array é passado para uma função, o truque do sizeof deixa de funcionar lá dentro, porque o array decai para um ponteiro.

Três regras práticas para dividir o trabalho em funções:

  • Um trabalho por função. Se o nome precisa de um "e" dentro dele, provavelmente são duas funções.
  • Receba o que você precisa, retorne o que você produz. Uma função que lê estado global é mais difícil de raciocinar e impossível de testar isoladamente.
  • Curta o bastante para ver de uma vez. Não existe número mágico de linhas, mas uma função que não cabe em uma tela normalmente esconde uma função menor dentro dela.

Funções chamando funções

Uma função pode chamar qualquer função que já tenha sido declarada, inclusive as que você escreveu:

A main chama report, que chama sumEvens, que chama isEven dentro de um laço. Cada chamada recebe seu próprio conjunto novo de variáveis locais, empilhado sobre o de quem chamou, e elas são descartadas quando ela retorna.

As definições acima estão em ordem de dependência - isEven antes de sumEvens antes de report - porque C exige que uma função seja declarada antes de ser chamada. Chame uma que apareça mais abaixo no arquivo e o compilador reclama de declaração implícita. A solução geral é um protótipo perto do topo do arquivo, o que deixa você ordenar as definições como quiser. Uma função chamar a si mesma também é legal e útil; isso é recursão.

main é uma função

main é uma função comum que o runtime chama por você. Seu tipo de retorno int é o status de saída do programa: 0 significa sucesso, diferente de zero significa falha, e scripts de shell e ferramentas de build o leem.

int main(void) {
    /* ... */
    return 0;
}

Desde o C99, cair no fim da main sem um return retorna 0 implicitamente - uma regra especial que vale só para a main. Toda outra função não-void que termina sem retornar um valor produz comportamento indefinido quando quem chamou usa o resultado.

A outra forma padrão é int main(int argc, char *argv[]), que recebe os argumentos de linha de comando. As duas estão corretas; void main() não está, digam o que disserem os tutoriais antigos.

Erros comuns

  • Esquecer o return em uma função não-void. Quem chamou então lê um valor de lixo. gcc -Wall avisa.
  • Declarar uma variável com o mesmo nome de um parâmetro dentro do corpo. Ela oculta o parâmetro, e a atribuição que você queria fazer na entrada não vai a lugar nenhum.
  • Esperar que uma função altere seus argumentos. C passa tudo por valor, então uma função recebe cópias. void reset(int x) { x = 0; } não muda nada no ponto da chamada - o assunto da próxima página.
  • Omitir os parênteses em uma chamada. printBanner; é uma expressão legal que avalia o endereço da função e o descarta. Compila, não faz nada, e o -Wall sinaliza.
  • Escrever () em vez de (void). Legal, mas desliga a verificação de argumentos em dialetos mais antigos de C.

Perguntas frequentes

Como se declara uma função em C?

Escreva o tipo de retorno, o nome e uma lista de parâmetros entre parênteses, e depois o corpo entre chaves: int add(int a, int b) { return a + b; }. Uma função que não recebe nada usa (void), e uma que não devolve nada tem o tipo de retorno void.

O que o return faz em uma função em C?

Ele encerra a função imediatamente e devolve um valor a quem chamou. O tipo do valor precisa combinar com o tipo de retorno declarado da função. Em uma função void você pode escrever um return; sem valor para sair mais cedo, ou omiti-lo e deixar a função terminar na chave de fechamento.

Por que a main retorna um int em C?

O valor de retorno da main é o status de saída do programa, que o sistema operacional e os scripts de shell conseguem ler. return 0; significa sucesso e qualquer valor diferente de zero significa falha. Desde o C99, chegar ao fim da main sem uma instrução return retorna 0 implicitamente.

Uma função em C pode retornar mais de um valor?

Não diretamente - return produz exatamente um valor. As saídas usuais são retornar uma struct contendo vários campos, ou passar ponteiros como parâmetros e fazer a função escrever os resultados extras por meio deles.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR