Noções básicas sobre funções C: parâmetros, retornos e escopo

7

A maioria das linguagens de programação permite dividir um programa confuso e de longa execução em partes organizadas e nomeadas. Esses pedaços são chamados de funções. Eles permitem reutilizar código em vez de reescrever a mesma lógica continuamente. Uma função recebe entradas, conhecidas como parâmetros, e gera um resultado. Em C, você pode passar quantos parâmetros precisar. O compilador não se importa com a ordem das funções no seu arquivo fonte, desde que saiba o nome da função antes de tentar executá-la.

Abordamos isso brevemente com a função rand. É a função mais simples possível. Não leva nada e devolve um número inteiro.

A linha int rand() informa ao compilador que rand é uma função. Não aceita parâmetros e retorna um número inteiro. Esta versão específica não possui variáveis ​​locais. Se assim fosse, eles ficariam logo abaixo da chave de abertura {. C permite que você declare variáveis ​​em qualquer lugar após um {. Eles existem até que o } correspondente seja atingido. Então eles desaparecem. Enquanto estão ativas, as variáveis ​​locais permanecem na pilha do sistema.

Observe a falta de ponto e vírgula após rand(). Se você inserir um acidentalmente, receberá uma enorme cascata de mensagens de erro do compilador que não fazem sentido. Mesmo sem parâmetros, você deve usar parênteses. Sem eles, você não está declarando uma função. Você está apenas declarando um número inteiro.

A declaração de retorno e vários pontos de saída

A instrução return é crítica para qualquer função que forneça um resultado. Ele define o valor a ser retornado e sai da função imediatamente. Você pode colocar várias instruções de retorno em uma única função para criar vários pontos de saída.

Se você esquecer a instrução return em uma função que deveria retornar um valor, a função será encerrada quando atingir a chave de fechamento. Ele retorna um valor aleatório e lixo. A maioria dos compiladores modernos irá avisá-lo se você não conseguir retornar um valor específico. C suporta o retorno de qualquer tipo: int, float, char, struct e mais.

Existem várias maneiras corretas de chamar a função rand. O mais comum é a atribuição:

Aqui, a variável x obtém o valor retornado por rand. Você deve incluir os parênteses na chamada, mesmo que não haja parâmetros. Sem eles, x recebe o endereço de memória da função rand. Isso raramente é o que você pretendia.

Você também pode chamá-lo dentro de uma condição:

Ou simplesmente ignore o resultado:

No último caso, a função é executada, mas o valor retornado é descartado. Talvez você não queira fazer isso com rand, mas muitas funções usam o valor de retorno como um código de erro. Se você sabe que um erro é impossível, não há problema em descartar o código.

Tratamento de devoluções nulas

Às vezes você não quer devolver nada. Você usa um tipo de retorno void para isso.

Esta função gera texto, mas não retorna nenhum valor. Você chama assim:

Novamente, inclua os parênteses. Se você os omitir, a função não será chamada. Ele será compilado corretamente em muitos sistemas, mas não fará nada.

Parâmetros e código C antigo

As funções C podem aceitar parâmetros de qualquer tipo. Aqui está uma função que calcula o fatorial de um número:

O parâmetro i é passado como um número inteiro. Para passar vários parâmetros, separe-os com vírgulas.

C evoluiu. Ocasionalmente, você verá códigos mais antigos escritos no “estilo antigo”. Parece assim:

Você precisa ser capaz de ler esta notação antiga. Ele é executado exatamente da mesma maneira. É apenas uma sintaxe diferente. Você deve usar o “novo estilo”, conhecido como ANSI C, onde os tipos são declarados na lista de parâmetros. Use o estilo antigo apenas se você souber que está enviando código para alguém com um compilador não-ANSI.

Por que isso é importante para usuários comuns

Você pode não escrever funções C diariamente. Mas entender como eles funcionam muda a forma como você pensa sobre software. Cada vez que você clica em um botão em um aplicativo da web, é provável que uma função processe esse clique, verifique parâmetros e retorne um status. Se você já viu um “erro de tempo de execução” ou um “estouro de pilha”, você encontrou os limites desses mecanismos.

A distinção entre passar um valor e passar uma referência (o endereço de memória) é uma fonte comum de bugs. Em linguagens de alto nível, isso geralmente fica oculto. Em C, é explícito. Saber que rand sem parênteses retorna um endereço, não um número, economiza horas de depuração.

O debate entre o estilo antigo e o novo estilo está em grande parte resolvido para novos projetos. ANSI C é o padrão. Mas ainda existem bases de código legadas. Saber ler int add(i,j) int i; int j; evita que você fique preso em um código que parece errado, mas funciona perfeitamente. É uma questão de alfabetização.

A pilha realmente importa para você? Se o seu programa travar inesperadamente, o rastreamento de pilha será sua única pista. Variáveis ​​locais que desaparecem quando atingem a chave de fechamento não são um bug. É um recurso. Ele mantém o uso da memória previsível.

Não existe uma maneira perfeita de escrever código. Existe apenas a maneira que compila e a maneira que não. Mantenha os parênteses. Observe seus retornos. E não presuma que o compilador irá salvá-lo de seus próprios erros de sintaxe. Não vai.