Programar em C/Funções

Origem: Wikilivros, livros abertos por um mundo aberto.



Tabela de conteúdo

[editar] Básico

[editar] O que é função

Uma função é um pedaço de código que faz alguma tarefa específica e pode ser chamado de qualquer parte do programa quantas vezes desejarmos.

Utilizamos funções para obter:

  • Clareza do código: separando pedaços de código da função main(), podemos entender mais facilmente o que cada parte do código faz. Além disso, para procurarmos por uma certa ação feita pelo programa, basta buscar a função correspondente. Isso torna muito mais fácil o ato de procurar por erros.
  • Reutilização: muitas vezes queremos executar uma certa tarefa várias vezes ao longo do programa. Repetir todo o código para essa operação é muito trabalhoso, e torna mais difícil a manutenção do código: se acharmos um erro nesse código, teremos que corrigí-lo em todas as repetições do código. Chamar uma função diversas vezes contorna esses dois problemas.
  • Independência: uma função é relativamente independente do código que a chamou. Uma função pode modificar variáveis globais ou ponteiros, mas limitando-se aos dados fornecidos pela chamada de função.

A idéia de uma função está, naturalmente, permitir você encapsular um idéia ou operação, dando um nome a ela, então chamar que operação de várias partes do resto de seu programa simplesmente usando o seu nome.

Estando corretamente projetado e estruturado o programa, em uma situação ideal, deverá ser possível modificar as funções sem efeito no resto do programa.

[editar] Definindo uma função

Uma função pode necessitar de alguns dados para que possa realizar alguma ação baseada neles. Esses dados são chamados parâmetros da função. Além disso, a função pode retornar um certo valor, que é chamado valor de retorno. Os parâmetros (e seus tipos) devem ser especificados explicitamente, assim como o tipo do valor de retorno.

A forma geral da definição de uma função é:

[tipo de retorno da função] [nome da função] (1º parâmetro, 2º parâmetro, …)
{
   //código
}
  • Para o nome da função e dos parâmetros valem as mesmas regras que foram dadas para os nomes de variáveis. Não podemos usar o mesmo nome para funções diferentes em um programa.
  • Todas as funções devem ser definidas antes da função main, ou deve ser feito o protótipo da função, que veremos mais adiante.
  • O código deve estar obrigatoriamente dentro das chaves e funciona como qualquer outro bloco.

[editar] Valor de retorno

Frequentemente, uma função faz algum tipo de processamento ou cálculo e precisa retornar o resultado desse procedimento. Em C, isso se chama valor de retorno e pode ser feito com a instrução return. Para poder retornar um valor, precisamos especificar seu tipo (char, int, float, double e variações). Para efetivamente retornar um valor, usamos a instrução return seguida do valor de retorno, que pode ou não vir entre parênteses. Um exemplo bem simples de função que retorna um valor inteiro:

int tres()
{
   return 3;  // poderia também ser return (3);
}

O tipo de retorno, além dos tipos normais de variáveis (char, int, float, double e suas variações), pode ser o tipo especial void, que na verdade significa que não há valor de retorno.

Nota Muitos livros dizem que a função main tem tipo de retorno void, o que não está correto. Segundo o padrão da linguagem C, a função main deve ter retorno do tipo int. Compiladores como o gcc darão mensagens de erro caso a função main() não seja definida corretamente.

[editar] Parâmetros

Como já foi dito, um parâmetro é um valor que é fornecido à função quando ela é chamada. É comum também chamar os parâmetros de argumentos, embora argumento esteja associado ao valor de um parâmetro.

Os parâmetros de uma função podem ser acessados da mesma maneira que variáveis locais. Eles na verdade funcionam exatamente como variáveis locais, e modificar um argumento não modifica o valor original no contexto da chamada de função, pois, ao dar um argumento numa chamada de função, ele é copiado como uma variável local da função. A única maneira de modificar o valor de um parâmetro é usar ponteiros, que serão introduzidos mais adiante.

Para declarar a presença de parâmetros, usamos uma lista de parâmetros entre parênteses, com os parâmetros separados por vírgulas. Cada declaração de parâmetro é feita de maneira semelhante à declaração de variáveis: a forma geral é tipo nome. Por exemplo:

int funcao (int a, int b)
float funcao (float preco, int quantidade)
double funcao (double angulo)

Para especificar que a função não usa nenhum parâmetro, a lista de parâmetros deve conter apenas a palavra-chave void. No entanto, ela é freqüentemente omitida nesses casos. Portanto, você poderia escrever qualquer uma destas duas linhas:

void funcao (void)
void funcao ()

Note que os nomes dos parâmetros são usados apenas na própria função (para distinguir os argumentos); eles não têm nenhuma relação com as variáveis usadas para chamar a função.

[editar] Chamadas de funções

Para executar uma função, fazemos uma chamada de função, que é uma instrução composta pelo nome da função, seguido pela lista de argumentos entre parênteses:

nome_da_função (arg1, arg2, ...);

Os argumentos podem ser qualquer tipo de expressão: podem ser variáveis, valores constantes, expressões matemáticas ou até mesmo outras chamadas de função.

Lembre que você deve sempre dar o mesmo número de argumentos que a função pede. Além disso, embora algumas conversões de tipo sejam feitas automaticamente pelo compilador, você deve atender aos tipos de argumentos.

Note que o valor dos argumentos é copiado para a função, de maneira que as variáveis originais ficam inalteradas mesmo que na função tentemos alterá-las. A isso chamamos passagem de argumentos por valor (ao contrário de por referência). Veremos como modificar as variáveis originais na seção Ponteiros.

A própria chamada de função também é uma expressão cujo valor é o valor de retorno da função, bastando colocá-la no lado direito de um sinal de igual para guardar o valor numa variável. Por exemplo, se a função "quadrado" retorna o quadrado de um número inteiro, podemos fazer assim para calcular o quadrado de 11 na variável x:

int x = quadrado (11);

[editar] Dois exemplos

#include <stdio.h>

int quadrado (int x)
{
   return (x * x);
}

void saudacao (void)
{
   printf ("Olá!\n");
}

void despedida (void)
{
   printf ("Fim do programa.\n");
}

int main ()
{
   int numero, resultado;
   saudacao ();

   printf ("Digite um número inteiro: ");
   scanf ("%d", &numero);
   resultado = quadrado (numero);
   printf ("O quadrado de %d é %d.\n", numero, resultado);

   despedida ();
   return 0;
}

Você veria na tela, ao executar o programa:

Olá!
O quadrado de 42 é 1764.
Fim do programa.

Repare que, ao chegar na chamada de uma função, o programa passa o controle para essa função e, após seu término, devolve o controle para a instrução seguinte na função original.

Mais um exemplo, com uma função de 3 argumentos:

#include <stdio.h>

/* Multiplica 3 numeros */
void mult (float a, float b, float c)
{
   printf ("%f",a*b*c);
}

int main ()
{
   float x, y;
   x = 23.5;
   y = 12.9;
   mult (x, y, 3.87);
   return 0;
}

[editar] Protótipo ou Declaração de função

Quando um programa C está sendo compilado e uma chamada de função é encontrada, o compilador precisa saber o tipo de retorno e os parâmetros da função, para que ele possa manipulá-los corretamente. O compilador só tem como saber isso se a função já tiver sido definida. Portanto, se tentarmos chamar uma função que está definida abaixo da linha onde estamos fazendo a chamada, ou mesmo em outro arquivo, o compilador dará uma mensagem de erro, pois não conseguiu reconhecer a função.

//Exemplo de erro de chamada de função
int main()
{
  int a = 1;
  int b = 2;
  soma(a,b); // erro: a função está definida abaixo desta linha!
}
void soma(int a, int b)
{
 cout << a+b;
}

Nesses casos, podemos declarar uma função antes de defini-la. Isso facilita o trabalho de usar diversas funções: você não precisará se importar com a ordem em que elas aparecem nos arquivos.

A declaração de função (também chamada de protótipo de função) nada mais é que a definição da função sem o bloco de código. Como uma instrução, ela deve ser seguida de um ponto-e-vírgula. Portanto, para declarar a função:

int quadrado (int x)
{
   return (x * x);
}

escreveríamos:

int quadrado (int x);

Numa declaração, também podemos omitir os nomes dos parâmetros, já que estes são ignorados por quem chama a função:

int quadrado (int);

Poderíamos, por exemplo, reorganizar o início do programa-exemplo dado um pouco acima, o que permitiria colocar as funções em qualquer ordem mesmo que houvesse interdependência entre elas:

#include <stdio.h>

int quadrado (int x);
void saudacao (void);
void despedida (void);

// seguem as funções do programa

Note que a definição da função não deve contradizer a declaração da mesma função. Se isso ocorrer, uma mensagem de erro será dada pelo compilador.

[editar] Variáveis locais versus globais

Quando declaramos as variáveis, nós podemos fazê-lo

  • Dentro de uma função ou
  • Fora de todas as funções inclusive a main().

As primeiras são as designadas como locais: só têm validade dentro do bloco no qual são declaradas. As últimas são as globais, elas estão vigentes em qualquer uma das funções.

Quando uma função tem uma variável local (ou parâmetro) com o mesmo nome de uma variável global a função dará preferência à variável local. Daqui conclui-se e bem que, podemos ter uma variáveis com o mesmo nome, o que contradiz o que nós dissemos no capitulo das variáveis. Então reformulamos: apenas na situação em que temos 2 variáveis locais é que é colocada a restrição de termos nomes diferentes caso contrário não conseguiríamos distinguir uma da outra.

Exemplo:
Então aonde estávamos??
Sim ,aqui "largo" e "alto" sao variáveis internas fazem parte de "minhaFuncion()".

/*espanhol para incultos :)*/
void minhaFuncion()
{
  double largo = 5;
  double alto = 6;
}

As variaveis largo e alto nao estao definidas aqui, isto quer dizer que elas nao tem nem um valor.
E nao podemos usar os valores definido dentro da "minhaFuncion".

void calcular()
{
  long surface = largo * alto; /*Error bip bip valor nao definido*/
   return(surface);
}

Entao podemos usar o valor das variáveis externas dentro de todas as funçoes :
Exemplo:

#include <stdio.h>
/*Variavel externa*/
long largo = 10;
long alto  = 20;
void minhaFuncion()
{
  /*variavel interna*/
  long somma = largo + alto ;
}
long calcular()
{
  long surface = largo * alto; 
}
int main(void)
{
 long reslt = calcular();
 printf("A surface e:%ld\n",reslt);
 return 0 ;
 }

Curiosidade

Em C++ existe a palavra reservada "auto", que serve para dizer que uma variável é local.
Mas é inútil pois as variáveis declaradas dentro de um bloco já são consideradas locais.

[editar] Passagem de parâmetros por valor e por referência

Recycle001.svg Esta página precisa ser reciclada (discuta).
Ao melhorá-la, você estará ajudando o Wikilivros.

O que nós temos feito quando chamamos uma função é a dita chamada por valor. Quer dizer, quando chamamos uma função e passamos parâmetros para a função protótipo e depois para a função definição, o valor dos argumentos passados são copiados para os parâmetros da função. Estes existem independentemente das variáveis que foram passadas. Eles tomam apenas uma cópia do valor passado, e se esse valor for alterado o valor dos argumentos passados não são alterados. Ou seja, não são alterados os valores dos parâmetros fora da função. Este tipo de chamada de função é denominado chamada (ou passagem de parâmetros) por valor.

Dito de outra maneira. Passamos a variável “a”, ela entra na definição da função como copia de “a” e entra como variável “b”. Se a variável “b” for alterada no decorrer da função, o valor de “a” não é alterado.

//Exemplo
#include <iostream>
using namespace std;
float quadrado(float num); //protótipo da função quadrado()
int main ()
{
  float num, res; //declaro 2 variáveis: num e res
  cout << “Entre com um numero: ";
  cin >> num; //associo o valor inserido á variável num
  res = quadrado(num); //chamo a função quadrado e passo o parâmetro num
  cout << "\n\nO numero original e:” << num;
  cout << ”O seu quadrado vale:” << res;
  float a, b;
  cout << “Entre com um numero: ";
  cin >> a;
  a = a*a;
  b = a;
  cout << "\n\nO numero original e:” << a;
  cout << "O seu quadrado vale:” << b ;
  system (“pause”);
  return 0;
}
float quadrado (float num) //descrição da função quadrado
{
  return num*num; //retorna num ao quadrado
} 

Quando a função main() é executada, ela chega a meio e vê uma chamada para a função quadrado() e onde é passado o parâmetro "num". Ela já estava a espera, pois "viu" o protótipo. Ela então vai executar a função que está depois da função do main(). E o que acontece é que o "num", vai ficar com o dobro do valor. Esse valor do main() vai entrar novamente no main(). E é associado á variável "res". Depois temos a impressão da variável "num" e "res". Ora o que acontece é que o valor do "num" fica igual ao valor antes de entrar na função. Fazemos a mesma coisa agora com a variável "a" e "b", e vemos que agora a função a é alterada. Resumindo, o valor variável quando entra numa outra função não é alterado (na passagem por valor).

Quando o valor do parâmetro é alterado denominamos chamada (ou passagem) por referência. O C não faz chamadas por referência. Mas podemos simular isto com outra arma do C que são os ponteiros, que serão melhor explicados mais adiante.

[editar] Os argumentos argc e argv

A função main(), como dissemos antes, é uma função especial. Introduzimo-la como uma função sem parâmetros; no entanto, ela também pode receber parâmetros formais. No entanto, o programador não pode escolher quais serão. Eles devem ser os seguintes:

int main (int argc, char *argv[])
  • argc (argument count) é um inteiro e possui o número de argumentos com os quais o programa foi chamado na linha de comando. Ele é no mínimo 1, pois o nome do programa é contado como sendo o primeiro argumento.
  • argv (argument values) é um ponteiro para uma matriz de strings (conceitos que serão abordados mais à frente). Cada string desta matriz é um dos parâmetros da linha de comando. argv[0] sempre aponta para o nome do programa (que, como já foi dito, é considerado o primeiro argumento). É para saber quantos elementos temos em argv que temos argc.

Como pode se imaginar, os nomes dos parâmetros "argc" e "argv" podem ser mudados, mas por questão de padronização não se costuma modificá-los.

Só não vamos ilustrar aqui o uso de argc e argv pois ainda não foram apresentadas as matrizes.

[editar] Avançado

[editar] VOID

Como dissemos uma função retorna um valor. E pode receber parâmetros. O void é utilizado da seguinte forma:

void função(void)
{
  //codigo  
}

No exemplo acima, a palavra void define que:

  • Não vai receber parâmetros nenhuns.
  • E não vai retornar qualquer valor.

Ou melhor, void é uma explicitação do programador que aquela função não vai receber ou retornar nenhum valor.
O valor da função é ignorado, mas a função realmente retorna um valor, por isso para que o resultado não seja interpretado como um erro é bom declarar void.

IMPORTANTE
Não se pode utilizar void na função principal main, apesar de existirem exemplos com void em algumas
bibliografias. Infelizmente alguns compiladores aceita void main(). O main() é especial e tem de
retornar um int, uma execução bem sucedida do programa costuma retornar 0 (zero), em caso de erro
retorna 1 (um).

[editar] Recursividade

Uma função pode chamar a si própria. Uma função assim é chamada função recursiva. Há várias operações matemáticas recursivas, das quais exemplos bem conhecidos são a seqüência de Fibonacci e o fatorial.

Daremos o exemplo do cálculo do fatorial de um número, definido como o produto de todos os números naturais (não nulos) menores ou iguais a ele — por exemplo, 5! (lê-se "cinco fatorial") é igual a 5 \times 4 \times 3 \times 2 \times 1. Atenção à convenção 0! = 1.

Uma maneira de definir o algoritmo de fatorial é:

n! = \begin{cases}
1,         & \mbox{se } n = 0 \mbox { ou } n = 1 \\
n(n - 1)!, & \mbox{se } n \ge 2 \\
\end{cases}

E a implementação correspondente seria esta:

#include <stdio.h>

int fatorial(int n)
{
   if (n >= 2)
      return n * fatorial(n - 1);
   else
      return 1;
}

int main()
{
   int n;
   printf("Digite um valor para n: ");
   scanf("%d", &n);
   printf("\nO fatorial de %d é %d", n, fatorial(n));
   return 0;
}


[editar] Parâmetros default (padrão)

Aqui a funçao "funDefa()" contida na função "main()" contem um valor default "nfunDefa( k,30)". Isto indica ao compilador que na ausência de argumento definido deve usar este valor. Parâmetros default não fazem parte da linguagem "C" , mais sim da "linguagem C++" . Por exemplo: Na linha 16 aonde esta escrito a função nfunDefa() , coloque nfunDefa(50,25). Conclusão este e um exemplo inútil na "linguagem C" .

#include <stdio.h>
#include <stdlib.h>
/*-----------------------------Cabeçalho--------------------------------*/
/*Definimos uma funçao*/
void nfunDefa(int pr,int se)
{
         printf("Meu Primeiro argumento:%d\n",pr );
         printf("Meu Segundo argumento :%d\n",se );
}
int main (void)
{
         int k=20,n=30;
         /*Na linguagem "C" a chamada de uma funçao
          *Deve conter a mesma quantidade de argumentos  que a funçao que esta esperando
          *Nao podemos colocar so k ou so n*/
         nfunDefa( k,30);         /*chamada "normal"*/
}

Na linguagem "C" os argumentos contido na função que faz a chamada deve ser equivalente ao número de argumentos definidos em a função que esta a esperar.
Por exemplo:

           funcion(int a,int b) ------> Dois parâmetros
           funcion(30)       ---------> incorreto ou correto so na linguagem C++
           funcion (25,58)   ---------> Dois parâmetros Correto nas duas linguagens "C" e C++

Então dentro da "função main" na função "funDefa()" não podemos definir só "n" ou só "k". A linguagem C++ por ser mais versátil nos permite.

[editar] Sobrecarga de funções

Em C++ duas funções podem ter o mesmo nome se:

  • Tiverem um nº diferente de parâmetros e/ou
  • Se os parâmetros forem de tipos diferentes (ints floats,..)

A função não pode ser overloaded apenas com diferentes tipo de retorno de função (ie, uma função retornar ints e a outra retornar floats) então os parâmetros é que interessam.

#include <iostream>
using namespace std;
void ConvertFToC(double f, double &c);
void ConvertFToC(float f, float &c);
void ConvertFToC(int f, int &c);
int main()
{
   double df, dc;
   float ff, fc;
   int i_f,i_c;    //if is a reserved word
   df = 75.0;
   ff = 75.0;
   i_f = 75;
   // The compiler resolves the correct
   // version of ConvertFToC based on 
   // the arguments in each call
   cout << "Calling ""double"" version" << endl;
   ConvertFToC(df,dc);
   cout << df << " == " << dc << endl << endl;
   cout << "Calling ""float"" version" << endl;
   ConvertFToC(ff,fc);
   cout << ff << " == " << fc << endl << endl;
   cout << "Calling ""int"" version" << endl;
   ConvertFToC(i_f,i_c);
   cout << i_f << " == " << i_c << endl << endl;
   system ("pause");
}
void ConvertFToC(double f, double &c)
{
   cout << "In ""double"" version" << endl;
   c = (f - 32.0) * 5. / 9.;
}
void ConvertFToC(float f, float &c)
{
   cout << "In ""float"" version" << endl;
   c = (f - 32.0) * 5. / 9.;
}
void ConvertFToC(int f, int &c)
{
   cout << "In ""int"" version" << endl;
   c = (f - 32) * 5. / 9.;
}

O que é que acontece se tivermos um nº diferente de argumentos entre a chamada e a definição?

A solução aqui proposta é quando não sabemos a quantidade de parâmetros que a função vai ser chamada ou mesmo a tipologia desses argumentos, o que se sugere é fazer várias definições para a função e dar a todas elas o mesmos nome, que o compilador vai saber escolher a definição correcta através do nº e tipologia de argumentos.

Entretanto, por boa prática, as funções não devem ser sobrecarregadas se fizerem operações distintas.

[editar] inline

Uma função inline, em vez de ser chamada, será movida para o local de chamada no momento da compilação.
Se fizermos um paralelismo com as directivas de compilação, como #define, ela vai substituir cada chamada da função pela própria função, é como fosse uma macro.
Mas isto só tem vantagens para, primeiro para códigos pequenos, e para quem necessite muito da velocidade no processamento. Alguns compiladores já fazem isto automaticamente.

Para tornar uma função inline basta preceder a declaração da função com o nome inline.

inline [tipo_de_retorno] [nome_da_função] (argumentos)
{
  //código  
}