Mudanças entre as edições de "SOP-funcoes"

De MediaWiki do Campus São José
Ir para navegação Ir para pesquisar
(New page: = Linguagem C: funções = Uma função corresponde a um algoritmo, que pode ser usado em diferentes partes de um programa. Um primeiro exemplo é mostrado a seguir: <syntaxhighlight lang=c> #include <s...)
 
 
(11 revisões intermediárias pelo mesmo usuário não estão sendo mostradas)
Linha 1: Linha 1:
 
= Linguagem C: funções =
 
= Linguagem C: funções =
  
Uma função corresponde a um algoritmo, que pode ser usado em diferentes partes de um programa. Um primeiro exemplo é mostrado a seguir:
+
Mas afinal para que servem funções ? Para propósitos como:
 +
 
 +
#'''Facilitar a reutilização de um algoritmo em diferentes partes de um programa:''' um exemplo são as funções [http://www.kernel.org/doc/man-pages/online/pages/man3/printf.3.html printf] e [http://www.kernel.org/doc/man-pages/online/pages/man3/scanf.3.html scanf], que fazem parte da [http://pt.wikipedia.org/wiki/Biblioteca_padr%C3%A3o_do_C biblioteca C padrão].
 +
#'''Reduzir a complexidade de um programa, ao dividi-lo em algoritmos menores e mais simples''': a divisão de um programa em pequenos algoritmos especializados o torna mais fácil de ser escrito, de ser lido e entendido, de ser depurado (debug) e mantido, e também de ser modificado.
 +
 
 +
== ola, mundo ! ==
 +
 
 +
Uma forma simples de entender funções é vê-las como algoritmos, que podem ser usados em diferentes partes de um programa, e mesmo em diferentes programas. Um primeiro exemplo é mostrado a seguir:
  
 
<syntaxhighlight lang=c>
 
<syntaxhighlight lang=c>
Linha 45: Linha 52:
 
  Ola, mundo !
 
  Ola, mundo !
  
Um outro exemplo é uma função para calcular o quadrado de um número:
+
== Funções com parâmetros e declaração de funções ==
 +
 
 +
Mas e se fosse desejável criar uma função que pudesse mostrar diferentes mensagens na tela ? O exemplo abaixo ilustra uma forma de fazer isto:
 +
 
 +
<syntaxhighlight lang=c>
 +
#include <stdio.h>
 +
 
 +
void mostra_mensagem(char * mensagem) {
 +
  printf("Sua mensagem: %s\n", mensagem);
 +
}
 +
 
 +
int main() {
 +
  mostra_mensagem("Ola, mundo");
 +
  mostra_mensagem("testando ...");
 +
  mostra_mensagem("");
 +
  mostra_mensagem("testando de novo ...");
 +
}
 +
</syntaxhighlight>
 +
 
 +
Se o programa acima for executado, dará este resultado:
 +
 
 +
> gcc -o mostra mostra.c
 +
> ./mostra
 +
Sua mensagem: Ola, mundo
 +
Sua mensagem: testando ...
 +
Sua mensagem:
 +
Sua mensagem: testando de novo ...
 +
 
 +
A função ''mostra_mensagem'' apresenta na tela uma string que é especificada no momento em que essa função é chamada. A isto se chama ''parametrização'': tornar um algoritmo independente de valores específicos de entrada, de forma que possa funcionar com qualquer valor de entrada compatível. O próximo exemplo aprofunda este conceito por meio de uma função que calcula o quadrado de um número:
 +
 
 +
<syntaxhighlight lang=c>
 +
#include <stdio.h>
 +
 
 +
int quadrado(int x) {
 +
  int y;
 +
 +
  y = x*x;
 +
  return y;
 +
}
 +
 
 +
int main() {
 +
  printf("O quadrado de 2 é %d\n", quadrado(2));
 +
  printf("O quadrado de 3 é %d\n", quadrado(3));
 +
}
 +
</syntaxhighlight>
 +
 
 +
Este segundo exemplo é mais elaborado do que os anteriores, pois a função ''quadrado'' possui um dado de entrada (o parâmetro ''int x'', também chamado de ''argumento da função''), tem uma variável local (''int y'', que existe somente dentro da função) e devolve um valor do tipo ''int''  como resultado. A declaração da função revela esses detalhes:
 +
 
 +
[[Image:Quadrado.png|300px]]
 +
 
 +
Esses são os elementos básicos para declarar, escrever e usar funções.
 +
 
 +
== Escopo de variáveis ==
 +
 
 +
* Texto obtido de: [http://www.inf.pucrs.br/~pinho/LaproI/Funcoes/AulaDeFuncoes.htm Uso de funções em C]
 +
 
 +
Por escopo de uma variável entende-se o bloco de código onde esta variável é válida. Com base nisto, temos as seguintes afirmações:
 +
* As variáveis valem no bloco que são definidas;
 +
* As variáveis definidas  dentro de uma função recebem o nome de '''''variáveis locais'''''
 +
* As variáveis declaradas fora de qualquer função são chamadas de '''''variáveis globais'''''
 +
* Os parâmetros formais de uma função (também conhecidos como ''argumentos da função'') valem também somente dentro da função;
 +
* Uma variável definida dentro de uma função não é acessível em outras funções, MESMO QUE ESSAS VARIÁVEIS TENHAM NOMES IDÊNTICOS.
 +
 
 +
==== Variáveis locais ====
 +
 
 +
No trecho de código a seguir tem-se um exemplo com funções e variáveis com nomes iguais.
 +
 
 +
<syntaxhighlight lang=c>
 +
#include <stdio.h>
 +
 
 +
void FUNC1() {
 +
  int B;
 +
 
 +
  B = -100;
 +
  printf("Valor de B dentro da função FUNC1: %d\n", B);
 +
}
 +
 
 +
void FUNC2() {
 +
  int B;
 +
 
 +
  B = -200;
 +
  printf("Valor de B dentro da função FUNC2: %d\n", B);
 +
}
 +
 
 +
void main() {
 +
  int B;
 +
 
 +
  B = 10;
 +
  printf("Valor de B: %d\n", B);
 +
  B = 20;
 +
  FUNC1();
 +
  printf("Valor de B: %d\n", B);
 +
  B = 30;
 +
  FUNC2();
 +
  printf("Valor de B: %d\n", B);
 +
}
 +
</syntaxhighlight>
 +
 
 +
O resultado de sua execução deve ser:
 +
 
 +
<syntaxhighlight lang=text>
 +
Valor de B: 10
 +
Valor de B dentro da função FUNC1: -100
 +
Valor de B: 20
 +
Valor de B dentro da função FUNC2: -200
 +
Valor de B: 30
 +
</syntaxhighlight>
 +
 
 +
==== Variáveis globais ====
 +
 
 +
O seguinte exemplo mostra o mesmo programa, porém fazendo com que a variável B seja global:
  
 
<syntaxhighlight lang=c>
 
<syntaxhighlight lang=c>
 
#include <stdio.h>
 
#include <stdio.h>
 +
 +
// Declaração da variável global B
 +
int B;
 +
 +
void FUNC1() {
 +
  B = -100;
 +
  printf("Valor de B dentro da função FUNC1: %d\n", B);
 +
}
 +
 +
void FUNC2() {
 +
  B = -200;
 +
  printf("Valor de B dentro da função FUNC2: %d\n", B);
 +
}
 +
 +
void main() {
 +
  B = 10;
 +
  printf("Valor de B: %d\n", B);
 +
  B = 20;
 +
  FUNC1();
 +
  printf("Valor de B: %d\n", B);
 +
  B = 30;
 +
  FUNC2();
 +
  printf("Valor de B: %d\n", B);
 +
}
 +
</syntaxhighlight>
 +
 +
O resultado de sua execução deve ser:
 +
 +
<syntaxhighlight lang=text>
 +
Valor de B: 10
 +
Valor de B dentro da função FUNC1: -100
 +
Valor de B: -100
 +
Valor de B dentro da função FUNC2: -200
 +
Valor de B: -200
 +
</syntaxhighlight>
 +
 +
E se existir um variável local  com mesmo nome que uma global ? nesse caso, a variável local vai ''esconder'' a sua homônima global:
 +
 +
<syntaxhighlight lang=c>
 +
#include <stdio.h>
 +
 +
// Declaração da variável global B
 +
int B;
 +
 +
void FUNC1() {
 +
  //  no escopo desta função, esta variável local vai esconder a variável global B!
 +
  int B;
 +
 +
  B = -100;
 +
  printf("Valor de B dentro da função FUNC1: %d\n", B);
 +
}
 +
 +
void FUNC2() {
 +
  B = -200;
 +
  printf("Valor de B dentro da função FUNC2: %d\n", B);
 +
}
 +
 +
void main() {
 +
  B = 10;
 +
  printf("Valor de B: %d\n", B);
 +
  B = 20;
 +
  FUNC1();
 +
  printf("Valor de B: %d\n", B);
 +
  B = 30;
 +
  FUNC2();
 +
  printf("Valor de B: %d\n", B);
 +
}
 +
</syntaxhighlight>
 +
 +
O resultado de sua execução deve ser:
 +
 +
<syntaxhighlight lang=text>
 +
Valor de B: 10
 +
Valor de B dentro da função FUNC1: -100
 +
Valor de B: 20
 +
Valor de B dentro da função FUNC2: -200
 +
Valor de B: -200
 +
</syntaxhighlight>
 +
 +
== Valor de retorno de função ==
 +
 +
Os próximos exemplos mostram outras situações em que se usam funções.
 +
 +
=== Função para testar se um número é par ou ímpar ===
 +
 +
<syntaxhighlight lang=c>
 +
#include <stdio.h>
 +
 +
int eh_par(int numero) {
 +
  int resto;
 +
 +
  resto = numero % 2;
 +
  if (resto == 0) return 1;
 +
  else return 0;
 +
}
 +
 +
int main() {
 +
  int x;
 +
 +
  printf("Digite um numero: ");
 +
  scanf("%d", &x);
 +
  if (eh_par(x)) {
 +
    printf("%d eh par\n", x);
 +
  } else {
 +
    printf("%d eh impar\n", x);
 +
  }
 +
 +
}
 +
</syntaxhighlight>
 +
 +
O exemplo acima mostra se um número é par ou ímpar. A função ''int eh_par(int numero)'' retorna ''1'' se o valor contido em ''numero'' for par, e ''0'' caso contrário. Veja como essa função é chamada dentro da função ''main'':
 +
 +
<syntaxhighlight lang=c>
 +
if (eh_par(x)) {
 +
</syntaxhighlight>
 +
 +
A função é usada diretamente como uma condição do ''if .. else'' (equivalente ao ''se .. então .. senão'' do Portugol). Isto é possível porque na linguagem C o valor 0 é equivalente ao valor booleano FALSO, e qualquer valor diferente de 0 é VERDADEIRO. Assim, ao retornar 0 ou 1, a função ''eh_par'' está tendo um resultado equivalente a FALSO ou VERDADEIRO. Tanto que o programa poderia ser escrito assim, tendo o mesmo resultado final:
 +
 +
<syntaxhighlight lang=c>
 +
int main() {
 +
  int x;
 +
 +
  printf("Digite um numero: ");
 +
  scanf("%d", &x);
 +
  if (! eh_par(x)) {
 +
    printf("%d eh impar\n", x);
 +
  } else {
 +
    printf("%d eh par\n", x);
 +
  }
 +
 +
}
 +
</syntaxhighlight>
 +
 +
Lembre que o operador ''!'' é equivalente a NÃO lógico (negação de uma condição). Então a condição ''! eh_par(x)'' é verdadeira se ''x'' não for par.
 +
 +
=== Função para testar se um número é primo ===
 +
 +
Um exemplo semelhante ao anterior trata da verificação se um número é primo:
 +
 +
<syntaxhighlight lang=c>
 +
#include <stdio.h>
 +
 +
int primo(int n) {
 +
  int x, ok;
 +
 +
  x = 2;
 +
  ok = 1;
 +
  while ((x <= (n/2)) && ok) {
 +
    if ((n % x) == 0) {
 +
      ok = 0;
 +
    } else x++;
 +
  }
 +
  return ok;
 +
}
 +
 +
int main() {
 +
  int n;
 +
 +
  printf("Numero: ");
 +
  scanf("%d", &n);
 +
  if (primo(n)) {
 +
    printf("%d eh primo\n", n);
 +
  } else {
 +
    printf("%d nao eh primo\n", n);
 +
  }
 +
}
 +
</syntaxhighlight>
 +
 +
A função ''int primo(int n)'' retorna 1 se o valor passado como parâmetro no argumento ''int n''for primo, e 0 caso contrário. Essa função possui um algoritmo mais elaborado que os exemplos anteriores, o qual utiliza uma estrutura de repetição com ''while (condição)'' (equivalente ao ''Enquanto condição'' do Portugol). Inclusive nessa função aparece um operador aritmético novo, usado para incrementar variáveis numéricas:
 +
 +
<syntaxhighlight lang=c>
 +
x++;
 +
</syntaxhighlight>
 +
 +
Isto é equivalente a:
 +
 +
<syntaxhighlight lang=c>
 +
x = x + 1;
 +
</syntaxhighlight>
 +
 +
De forma análoga, existe um operador parecido para decremento:
 +
 +
<syntaxhighlight lang=c>
 +
x--;
 +
</syntaxhighlight>
 +
 +
... equivalente a:
 +
 +
<syntaxhighlight lang=c>
 +
x = x - 1;
 +
</syntaxhighlight>
 +
 +
=== Função para fatorar um número ===
 +
 +
Esse exemplo para testar se números são primos pode ser aproveitado para criar um programa que fatore um número em seus divisores primos:
 +
 +
<syntaxhighlight lang=c>
 +
#include <stdio.h>
 +
 +
int primo(int n) {
 +
  int x, ok;
 +
 +
  x = 2;
 +
  ok = 1;
 +
  while ((x <= (n/2)) && ok) {
 +
    if ((n % x) == 0) {
 +
      ok = 0;
 +
    } else x++;
 +
  }
 +
  return ok;
 +
}
 +
 +
int main() {
 +
  int p, x;
 +
 +
  printf("Numero: ");
 +
  scanf("%d", &x);
 +
  p = 1;
 +
  while (x != 1) {
 +
    do {
 +
      p ++;
 +
    } while(!primo(p));
 +
 +
    while ((x % p) == 0) {
 +
      x = x /p;
 +
      printf("%d\n", p);
 +
    }
 +
  }
 +
}
 +
 +
</syntaxhighlight>
 +
 +
A função ''int primo(int n)'' foi reaproveitada para escrever esse novo programa, que mostra todos os fatores primos que dividem um número.
 +
 +
== Passagem de parâmetros ==
 +
 +
O próximo exemplo mostra uma função que torna maiúscula a primeira letra de uma ''string'':
 +
 +
<syntaxhighlight lang=c>
 +
#include <stdio.h>
 +
 +
void faz_primeira_letra_maiuscula(char * nome) {
 +
  if ((nome[0] >= 'a') && (nome[0] <= 'z')) {
 +
    nome[0] = nome[0] - 32;
 +
  }
 +
}
 +
 +
int main() {
 +
  char nome[32];
 +
 +
  printf("Digite um nome: ");
 +
  scanf("%s", nome);
 +
  faz_primeira_letra_maiuscula(nome);
 +
  printf("Nome: %s\n", nome);
 +
}
 +
</syntaxhighlight>
 +
 +
A função ''void faz_primeira_letra_maiuscula(char * nome)'' converte a primeira letra do parâmetro ''char * nome'' para maiúscula. A conversão se baseia na [http://pt.wikipedia.org/wiki/Ascii tabela ASCII], que associa códigos numéricos a caracteres. Mas a novidade nessa função é a possibilidade de modificar um valor passado como parâmetro, o que não acontecia nas funções vistas até então. Dependendo de como foi passado um parâmetro, modificações em seu valor dentro de uma função podem ou não refletir fora dessa função.
 +
 +
#*'''Passagem por valor:''' o valor da variável passada como parâmetro é copiado para uma variável local da função. Com isto, qualquer modificação feita na variável local da função não afeta a variável que foi passada como parâmetro. Ex:<syntaxhighlight lang=c>int incrementa_e_mostra(int x) {
 +
  x = x + 1;
 +
  printf("x=%d\n", x);
 +
}
 +
 +
int main() {
 +
  int contador = 0;
 +
 +
  incrementa_e_mostra(contador);
 +
  incrementa_e_mostra(contador);
 +
  incrementa_e_mostra(contador);
 +
  printf("Ao final, contador=%d\n", contador);
 +
}
 +
</syntaxhighlight>Ao executar este programa exemplo, o resultado na tela deve ser o seguinte:<br>''x=1''<br>''x=1''<br>''x=1''<br>''Ao final, contador=0''<br>... o que evidencia que o valor de ''contador'' não foi modificado dentro da função ''incrementa_e_mostra''.
 +
#*'''Passagem por referência:''' a própria variável é passada como parâmetro (quer dizer, a sua localização em memória, por isto se diz ''por referência''). Assim, qualquer modificação feita dentro da função chamada refletirá na variável que foi passada como parâmetro. Ex:<syntaxhighlight lang=c>int incrementa_e_mostra(int * x) {
 +
  *x = *x + 1;
 +
  printf("x=%d\n", *x);
 +
}
 +
 +
int main() {
 +
  int contador = 0;
 +
 +
  incrementa_e_mostra(&contador);
 +
  incrementa_e_mostra(&contador);
 +
  incrementa_e_mostra(&contador);
 +
  printf("Ao final, contador=%d\n", contador);
 +
}</syntaxhighlight>Ao se executar o exemplo acima, o resultado na tela deve ser:<br>''x=1''<br>''x=2''<br>''x=3''<br>''Ao final, contador=3''<br>Fica evidente que o valor da variável ''contador'' foi modificado dentro da função ''incrementa_e_mostra''. Repare que algumas diferenças de sintaxe existem quando se usa passagem por referência:
 +
#**A declaração do parâmetro na função deve ter um ''*'' (asterisco) antes do nome do parâmetro (ex: ''incrementa_e_mostra(int * x)'' )
 +
#**Dentro da função deve-se preceder com ''*'' (asterisco) o nome da variável do parâmetro (ex: ''*x = *x + 1;'')
 +
#***Quando a variável passada como parâmetro for do tipo ''struct'', ao invés de precedê-la por ''*'' (asterisco), deve-se usar a seguinte sintaxe: ''nome_variável->campo'' ao invés de ''nome_variável.campo''. Veja a função ''adiciona_pessoa'' do cadastro como exemplo.
 +
#**Na chamada da função, deve-se preceder com ''&'' o nome da variável passada como parâmetro (ex: ''incrementa_e_mostra(&contador);'' )
 +
#**As duas últimas regras de sintaxe acima não valem quando se passa uma variável ''string'' como parâmetro. Isto porque uma variável ''string'' por definição já é uma variável que contém um endereço de memória (quer dizer, ela já funciona como uma referência aos dados). Na verdade, isto vale para qualquer variável do tipo vetor (lembre que a ''string'' é um vetor de ''char''). Ex: <syntaxhighlight lang=c>void faz_primeira_letra_maiuscula(char * nome) {
 +
  if ((nome[0] >= 97) && (nome[0] <= 122)) {
 +
    nome[0] = nome[0] - 32;
 +
  }
 +
}
 +
 +
int main() {
 +
  char nome[32];
 +
 +
  printf("Digite um nome: ");
 +
  scanf("%s", nome);
 +
  faz_primeira_letra_maiuscula(nome);
 +
  printf("Nome: %s\n", nome);
 +
}</syntaxhighlight>
 +
#*'''No caso do exemplo do cadastro:''' para ver a diferença entre ''passagem de parâmetro por referência'' e ''por valor'' no caso do cadastro, veja [[media:Cadastro4.txt|esta versão do cadastro]] em que a função ''adiciona_pessoa'' faz passagem por valor.
 +
#*Veja no curso de linguagem C da UFMG uma discussão mais aprofundada sobre [http://www.sj.ifsc.edu.br/~msobral/SOP/docs/C_ufmg/aulas/c770.html passagem de parâmetros] e [http://www.sj.ifsc.edu.br/~msobral/SOP/docs/C_ufmg/aulas/c760.html escopo de variáveis].
 +
 +
== Reaproveitamento e especialização de funções ==
 +
 +
Uma função pode ser especializada para fazer uma tarefa mais limitada. Veja o exemplo da inicial maíuscula:
 +
 +
<syntaxhighlight lang=c>
 +
#include <stdio.h>
 +
 +
void faz_letra_maiuscula(char * nome, int pos) {
 +
  if ((nome[pos] >= 97) && (nome[pos] <= 122)) {
 +
    nome[pos] = nome[pos] - 32;
 +
  }
 +
}
 +
 +
void faz_primeira_letra_maiuscula(char * nome) {
 +
  faz_letra_maiuscula(nome, 0);
 +
}
 +
 +
int main() {
 +
  char nome[32];
 +
 +
  printf("Digite um nome: ");
 +
  scanf("%s", nome);
 +
  faz_primeira_letra_maiuscula(nome);
 +
  printf("Nome: %s\n", nome);
 +
}
 +
</syntaxhighlight>
 +
 +
Apesar de o resultado ser exatamente o mesmo do exemplo anterior, em que havia somente a função ''faz_primeira_letra_maiuscula'', esse novo exemplo mostra como especializar uma função mais geral. A função ''void faz_letra_maiuscula(char * nome, int posicao)'' torna maiúscula a letra contida na posição ''pos'' da ''string'' 'nome''. Quer dizer, ''faz_letra_maiuscula'' é capaz de tornar maiúscula qualquer letra da ''string'' contida em ''nome''. A função ''void faz_primeira_letra_maiuscula(char * nome)'' torna maiúscula a letra inicial da ''string'' contida em ''nome'', e para fazer isto agora foi usada a função ''faz_letra_maiuscula'', indicando-se que se quer mudar a letra na posição ''0''.
 +
 +
== Funções que usam tipos de dados definidos pelo usuário (ou ''struct'') ==
 +
 +
Funções podem receber parâmetros de tipos ''struct'', como retornar valores ''struct''. Veja esse exemplo do cálculo da distância entre dois pontos bidimensionais:
 +
 +
<syntaxhighlight lang=c>
 +
#include <stdio.h>
 +
#include <math.h>
 +
 +
struct Ponto {
 +
  float x,y;
 +
};
  
 
float quadrado(float x) {
 
float quadrado(float x) {
 
   return x*x;
 
   return x*x;
 +
}
 +
 +
float distancia(struct Ponto p1, struct Ponto p2) {
 +
  float d;
 +
 +
  d = sqrt(quadrado(p1.x - p2.x) + quadrado(p1.y - p2.y));
 
}
 
}
  
 
int main() {
 
int main() {
   printf("O quadrado de 2 é %f\n", quadrado(2));
+
  struct Ponto p1, p2;
 +
 
 +
  printf("Primeiro ponto:\n");
 +
  printf("  x=");
 +
  scanf("%f", &p1.x);
 +
   printf(" y=");
 +
  scanf("%f", &p1.y);
 +
 
 +
  printf("Segundo ponto:\n");
 +
  printf("  x=");
 +
  scanf("%f", &p2.x);
 +
  printf("  y=");
 +
  scanf("%f", &p2.y);
 +
 
 +
  printf("\nDistancia entre (%f, %f) e (%f, %f): %f\n", p1.x, p1.y, p2.x, p2.y,
 +
          distancia(p1, p2));
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
 +
Sua compilação e execução seguem abaixo:
 +
 +
> gcc -o distancia -lm distancia1.c 
 +
> ./distancia
 +
Primeiro ponto:
 +
  x=1
 +
  y=5
 +
Segundo ponto:
 +
  x=4
 +
  y=9
 +
Distancia entre (1.000000, 5.000000) e (4.000000, 9.000000): 5.000000
 +
 +
Observe que a compilação tem uma diferença em relação aos outros exemplos:  a opção ''-lm'' passada ao compilador. Essa opção informa ao compilador que o programa precisa da bilioteca de funções matemáticas (chamada de ''libm'', que vem de ''library math''). Uma biblioteca é um arquivo que contém funções prontas para serem usadas. No exemplo  acima, foi usada a função ''sqrt'' (abreviação de ''square root'', ou ''raiz quadrada''), que está implementada nessa biblioteca.
 +
 +
A função ''float distancia(struct Ponto p1, struct Ponto p2)'' calcula a distância entre os pontos dados pelos parâmetros ''p1'' e ''p2''. Esses parâmetros são do tipo ''struct Ponto'', que foi definido pelo usuário da seguinte forma:
 +
 +
<syntaxhighlight lang=c>
 +
struct Ponto {
 +
  float x,y;
 +
};
 +
</syntaxhighlight>
 +
 +
O programa mostrado acima pode ser melhorado, para pareça mais simples e fácil de ser entendido:
 +
 +
<syntaxhighlight lang=c>
 +
#include <stdio.h>
 +
#include <math.h>
 +
 +
struct Ponto {
 +
  float x,y;
 +
};
 +
 +
float quadrado(float x) {
 +
  return x*x;
 +
}
 +
 +
float distancia(struct Ponto p1, struct Ponto p2) {
 +
  float d;
 +
 +
  d = sqrt(quadrado(p1.x - p2.x) + quadrado(p1.y - p2.y));
 +
}
 +
 +
void le_ponto(char * mensagem, struct Ponto * p) {
 +
  printf("%s\n", mensagem);
 +
  printf("  x=");
 +
  scanf("%f", &p->x);
 +
  printf("  y=");
 +
  scanf("%f", &p->y);
 +
}
 +
 +
int main() {
 +
  struct Ponto p1, p2;
 +
 +
  le_ponto("Primeiro ponto:", &p1);
 +
  le_ponto("Segundo ponto:", &p2);
 +
 +
  printf("\nDistancia entre (%f, %f) e (%f, %f): %f\n", p1.x, p1.y, p2.x, p2.y,
 +
          distancia(p1, p2));
 +
}
 +
</syntaxhighlight>
 +
 +
A diferença em relação ao exemplo anterior está no aparecimento da função ''void le_ponto(char * mensagem, struct Ponto * p)'', que lê do teclado as coordenadas ''X'' e ''Y'' de um ponto. Essa função precisa fazer passagem de parâmetro por referência, uma vez que os campos ''x'' e ''y'' do argumento da função ''struct Ponto p'' devem ser modificados dentro da função, e essa modificação deve ser refletida fora da função. Veja que o acesso a esses campos tem uma sintaxe diferente:
 +
 +
<syntaxhighlight lang=c>
 +
  printf("  x=");
 +
  scanf("%f", &p->x);
 +
</syntaxhighlight>
 +
 +
Sempre que um argumento do tipo ''struct'' for passado por referência (ex: ''struct Ponto * p''), o acesso a seus campos deve usar essa sintaxe.

Edição atual tal como às 15h48min de 30 de novembro de 2010

Linguagem C: funções

Mas afinal para que servem funções ? Para propósitos como:

  1. Facilitar a reutilização de um algoritmo em diferentes partes de um programa: um exemplo são as funções printf e scanf, que fazem parte da biblioteca C padrão.
  2. Reduzir a complexidade de um programa, ao dividi-lo em algoritmos menores e mais simples: a divisão de um programa em pequenos algoritmos especializados o torna mais fácil de ser escrito, de ser lido e entendido, de ser depurado (debug) e mantido, e também de ser modificado.

ola, mundo !

Uma forma simples de entender funções é vê-las como algoritmos, que podem ser usados em diferentes partes de um programa, e mesmo em diferentes programas. Um primeiro exemplo é mostrado a seguir:

#include <stdio.h>

void ola() {
  printf("Ola, mundo !\n");
}

int main() {
  ola();
}

Ao se executar o programa acima, o resultado é aparecer na tela a mensagem "Ola, mundo !":

> gcc -o ola ola.c
> ./ola
Ola, mundo !

Se o exemplo for modificado da seguinte forma:

#include <stdio.h>

void ola() {
  printf("Ola, mundo !\n");
}

int main() {
  ola();
  ola();
  ola();
}

... sua execução dará como resultado:

> gcc -o ola ola.c
> ./ola
Ola, mundo !
Ola, mundo !
Ola, mundo !

Funções com parâmetros e declaração de funções

Mas e se fosse desejável criar uma função que pudesse mostrar diferentes mensagens na tela ? O exemplo abaixo ilustra uma forma de fazer isto:

#include <stdio.h>

void mostra_mensagem(char * mensagem) {
  printf("Sua mensagem: %s\n", mensagem);
}

int main() {
  mostra_mensagem("Ola, mundo");
  mostra_mensagem("testando ...");
  mostra_mensagem("");
  mostra_mensagem("testando de novo ...");
}

Se o programa acima for executado, dará este resultado:

> gcc -o mostra mostra.c
> ./mostra
Sua mensagem: Ola, mundo
Sua mensagem: testando ...
Sua mensagem:
Sua mensagem: testando de novo ...

A função mostra_mensagem apresenta na tela uma string que é especificada no momento em que essa função é chamada. A isto se chama parametrização: tornar um algoritmo independente de valores específicos de entrada, de forma que possa funcionar com qualquer valor de entrada compatível. O próximo exemplo aprofunda este conceito por meio de uma função que calcula o quadrado de um número:

#include <stdio.h>

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

int main() {
  printf("O quadrado de 2 é %d\n", quadrado(2));
  printf("O quadrado de 3 é %d\n", quadrado(3));
}

Este segundo exemplo é mais elaborado do que os anteriores, pois a função quadrado possui um dado de entrada (o parâmetro int x, também chamado de argumento da função), tem uma variável local (int y, que existe somente dentro da função) e devolve um valor do tipo int como resultado. A declaração da função revela esses detalhes:

Quadrado.png

Esses são os elementos básicos para declarar, escrever e usar funções.

Escopo de variáveis

Por escopo de uma variável entende-se o bloco de código onde esta variável é válida. Com base nisto, temos as seguintes afirmações:

  • As variáveis valem no bloco que são definidas;
  • As variáveis definidas dentro de uma função recebem o nome de variáveis locais
  • As variáveis declaradas fora de qualquer função são chamadas de variáveis globais
  • Os parâmetros formais de uma função (também conhecidos como argumentos da função) valem também somente dentro da função;
  • Uma variável definida dentro de uma função não é acessível em outras funções, MESMO QUE ESSAS VARIÁVEIS TENHAM NOMES IDÊNTICOS.

Variáveis locais

No trecho de código a seguir tem-se um exemplo com funções e variáveis com nomes iguais.

#include <stdio.h>

void FUNC1() {
   int B;

   B = -100;
   printf("Valor de B dentro da função FUNC1: %d\n", B);
}

void FUNC2() {
   int B;

   B = -200;
   printf("Valor de B dentro da função FUNC2: %d\n", B);
}

void main() {
   int B;

   B = 10;
   printf("Valor de B: %d\n", B);
   B = 20;
   FUNC1();
   printf("Valor de B: %d\n", B);
   B = 30;
   FUNC2();
   printf("Valor de B: %d\n", B);
}

O resultado de sua execução deve ser:

Valor de B: 10
Valor de B dentro da função FUNC1: -100
Valor de B: 20
Valor de B dentro da função FUNC2: -200
Valor de B: 30

Variáveis globais

O seguinte exemplo mostra o mesmo programa, porém fazendo com que a variável B seja global:

#include <stdio.h>

// Declaração da variável global B
int B;

void FUNC1() {
   B = -100;
   printf("Valor de B dentro da função FUNC1: %d\n", B);
}

void FUNC2() {
   B = -200;
   printf("Valor de B dentro da função FUNC2: %d\n", B);
}

void main() {
   B = 10;
   printf("Valor de B: %d\n", B);
   B = 20;
   FUNC1();
   printf("Valor de B: %d\n", B);
   B = 30;
   FUNC2();
   printf("Valor de B: %d\n", B);
}

O resultado de sua execução deve ser:

Valor de B: 10
Valor de B dentro da função FUNC1: -100
Valor de B: -100
Valor de B dentro da função FUNC2: -200
Valor de B: -200

E se existir um variável local com mesmo nome que uma global ? nesse caso, a variável local vai esconder a sua homônima global:

#include <stdio.h>

// Declaração da variável global B
int B;

void FUNC1() {
   //  no escopo desta função, esta variável local vai esconder a variável global B! 
   int B; 

   B = -100;
   printf("Valor de B dentro da função FUNC1: %d\n", B);
}

void FUNC2() {
   B = -200;
   printf("Valor de B dentro da função FUNC2: %d\n", B);
}

void main() {
   B = 10;
   printf("Valor de B: %d\n", B);
   B = 20;
   FUNC1();
   printf("Valor de B: %d\n", B);
   B = 30;
   FUNC2();
   printf("Valor de B: %d\n", B);
}

O resultado de sua execução deve ser:

Valor de B: 10
Valor de B dentro da função FUNC1: -100
Valor de B: 20
Valor de B dentro da função FUNC2: -200
Valor de B: -200

Valor de retorno de função

Os próximos exemplos mostram outras situações em que se usam funções.

Função para testar se um número é par ou ímpar

#include <stdio.h>

int eh_par(int numero) {
  int resto;

  resto = numero % 2;
  if (resto == 0) return 1;
  else return 0;
}

int main() {
  int x;

  printf("Digite um numero: ");
  scanf("%d", &x);
  if (eh_par(x)) {
    printf("%d eh par\n", x);
  } else {
    printf("%d eh impar\n", x);
  }

}

O exemplo acima mostra se um número é par ou ímpar. A função int eh_par(int numero) retorna 1 se o valor contido em numero for par, e 0 caso contrário. Veja como essa função é chamada dentro da função main:

if (eh_par(x)) {

A função é usada diretamente como uma condição do if .. else (equivalente ao se .. então .. senão do Portugol). Isto é possível porque na linguagem C o valor 0 é equivalente ao valor booleano FALSO, e qualquer valor diferente de 0 é VERDADEIRO. Assim, ao retornar 0 ou 1, a função eh_par está tendo um resultado equivalente a FALSO ou VERDADEIRO. Tanto que o programa poderia ser escrito assim, tendo o mesmo resultado final:

int main() {
  int x;

  printf("Digite um numero: ");
  scanf("%d", &x);
  if (! eh_par(x)) {
    printf("%d eh impar\n", x);
  } else {
    printf("%d eh par\n", x);
  }

}

Lembre que o operador ! é equivalente a NÃO lógico (negação de uma condição). Então a condição ! eh_par(x) é verdadeira se x não for par.

Função para testar se um número é primo

Um exemplo semelhante ao anterior trata da verificação se um número é primo:

#include <stdio.h>

int primo(int n) {
  int x, ok;

  x = 2;
  ok = 1;
  while ((x <= (n/2)) && ok) {
    if ((n % x) == 0) {
      ok = 0;
    } else x++;
  }
  return ok;
}

int main() {
  int n;

  printf("Numero: ");
  scanf("%d", &n);
  if (primo(n)) {
    printf("%d eh primo\n", n);
  } else {
    printf("%d nao eh primo\n", n);
  }
}

A função int primo(int n) retorna 1 se o valor passado como parâmetro no argumento int nfor primo, e 0 caso contrário. Essa função possui um algoritmo mais elaborado que os exemplos anteriores, o qual utiliza uma estrutura de repetição com while (condição) (equivalente ao Enquanto condição do Portugol). Inclusive nessa função aparece um operador aritmético novo, usado para incrementar variáveis numéricas:

x++;

Isto é equivalente a:

x = x + 1;

De forma análoga, existe um operador parecido para decremento:

x--;

... equivalente a:

x = x - 1;

Função para fatorar um número

Esse exemplo para testar se números são primos pode ser aproveitado para criar um programa que fatore um número em seus divisores primos:

#include <stdio.h>

int primo(int n) {
  int x, ok;

  x = 2;
  ok = 1;
  while ((x <= (n/2)) && ok) {
    if ((n % x) == 0) {
      ok = 0;
    } else x++;
  }
  return ok;
}

int main() {
  int p, x;

  printf("Numero: ");
  scanf("%d", &x);
  p = 1;
  while (x != 1) {
    do {
      p ++;
    } while(!primo(p));

    while ((x % p) == 0) {
      x = x /p;
      printf("%d\n", p);
    }
  }
}

A função int primo(int n) foi reaproveitada para escrever esse novo programa, que mostra todos os fatores primos que dividem um número.

Passagem de parâmetros

O próximo exemplo mostra uma função que torna maiúscula a primeira letra de uma string:

#include <stdio.h>

void faz_primeira_letra_maiuscula(char * nome) {
  if ((nome[0] >= 'a') && (nome[0] <= 'z')) {
    nome[0] = nome[0] - 32;
  }
}

int main() {
  char nome[32];

  printf("Digite um nome: ");
  scanf("%s", nome);
  faz_primeira_letra_maiuscula(nome);
  printf("Nome: %s\n", nome);
}

A função void faz_primeira_letra_maiuscula(char * nome) converte a primeira letra do parâmetro char * nome para maiúscula. A conversão se baseia na tabela ASCII, que associa códigos numéricos a caracteres. Mas a novidade nessa função é a possibilidade de modificar um valor passado como parâmetro, o que não acontecia nas funções vistas até então. Dependendo de como foi passado um parâmetro, modificações em seu valor dentro de uma função podem ou não refletir fora dessa função.

    • Passagem por valor: o valor da variável passada como parâmetro é copiado para uma variável local da função. Com isto, qualquer modificação feita na variável local da função não afeta a variável que foi passada como parâmetro. Ex:
      int incrementa_e_mostra(int x) {
        x = x + 1;
        printf("x=%d\n", x);
      }
      
      int main() {
        int contador = 0;
      
        incrementa_e_mostra(contador);
        incrementa_e_mostra(contador);
        incrementa_e_mostra(contador);
        printf("Ao final, contador=%d\n", contador);
      }
      
      Ao executar este programa exemplo, o resultado na tela deve ser o seguinte:
      x=1
      x=1
      x=1
      Ao final, contador=0
      ... o que evidencia que o valor de contador não foi modificado dentro da função incrementa_e_mostra.
    • Passagem por referência: a própria variável é passada como parâmetro (quer dizer, a sua localização em memória, por isto se diz por referência). Assim, qualquer modificação feita dentro da função chamada refletirá na variável que foi passada como parâmetro. Ex:
      int incrementa_e_mostra(int * x) {
        *x = *x + 1;
        printf("x=%d\n", *x);
      }
      
      int main() {
        int contador = 0;
      
        incrementa_e_mostra(&contador);
        incrementa_e_mostra(&contador);
        incrementa_e_mostra(&contador);
        printf("Ao final, contador=%d\n", contador);
      }
      
      Ao se executar o exemplo acima, o resultado na tela deve ser:
      x=1
      x=2
      x=3
      Ao final, contador=3
      Fica evidente que o valor da variável contador foi modificado dentro da função incrementa_e_mostra. Repare que algumas diferenças de sintaxe existem quando se usa passagem por referência:
      • A declaração do parâmetro na função deve ter um * (asterisco) antes do nome do parâmetro (ex: incrementa_e_mostra(int * x) )
      • Dentro da função deve-se preceder com * (asterisco) o nome da variável do parâmetro (ex: *x = *x + 1;)
        • Quando a variável passada como parâmetro for do tipo struct, ao invés de precedê-la por * (asterisco), deve-se usar a seguinte sintaxe: nome_variável->campo ao invés de nome_variável.campo. Veja a função adiciona_pessoa do cadastro como exemplo.
      • Na chamada da função, deve-se preceder com & o nome da variável passada como parâmetro (ex: incrementa_e_mostra(&contador); )
      • As duas últimas regras de sintaxe acima não valem quando se passa uma variável string como parâmetro. Isto porque uma variável string por definição já é uma variável que contém um endereço de memória (quer dizer, ela já funciona como uma referência aos dados). Na verdade, isto vale para qualquer variável do tipo vetor (lembre que a string é um vetor de char). Ex:
        void faz_primeira_letra_maiuscula(char * nome) {
          if ((nome[0] >= 97) && (nome[0] <= 122)) {
            nome[0] = nome[0] - 32;
          }
        }
        
        int main() {
          char nome[32];
        
          printf("Digite um nome: ");
          scanf("%s", nome);
          faz_primeira_letra_maiuscula(nome);
          printf("Nome: %s\n", nome);
        }
        
    • No caso do exemplo do cadastro: para ver a diferença entre passagem de parâmetro por referência e por valor no caso do cadastro, veja esta versão do cadastro em que a função adiciona_pessoa faz passagem por valor.
    • Veja no curso de linguagem C da UFMG uma discussão mais aprofundada sobre passagem de parâmetros e escopo de variáveis.

Reaproveitamento e especialização de funções

Uma função pode ser especializada para fazer uma tarefa mais limitada. Veja o exemplo da inicial maíuscula:

#include <stdio.h>

void faz_letra_maiuscula(char * nome, int pos) {
  if ((nome[pos] >= 97) && (nome[pos] <= 122)) {
    nome[pos] = nome[pos] - 32;
  }
}

void faz_primeira_letra_maiuscula(char * nome) {
  faz_letra_maiuscula(nome, 0);
}

int main() {
  char nome[32];

  printf("Digite um nome: ");
  scanf("%s", nome);
  faz_primeira_letra_maiuscula(nome);
  printf("Nome: %s\n", nome);
}

Apesar de o resultado ser exatamente o mesmo do exemplo anterior, em que havia somente a função faz_primeira_letra_maiuscula, esse novo exemplo mostra como especializar uma função mais geral. A função void faz_letra_maiuscula(char * nome, int posicao) torna maiúscula a letra contida na posição pos da string 'nome. Quer dizer, faz_letra_maiuscula é capaz de tornar maiúscula qualquer letra da string contida em nome. A função void faz_primeira_letra_maiuscula(char * nome) torna maiúscula a letra inicial da string contida em nome, e para fazer isto agora foi usada a função faz_letra_maiuscula, indicando-se que se quer mudar a letra na posição 0.

Funções que usam tipos de dados definidos pelo usuário (ou struct)

Funções podem receber parâmetros de tipos struct, como retornar valores struct. Veja esse exemplo do cálculo da distância entre dois pontos bidimensionais:

#include <stdio.h>
#include <math.h>

struct Ponto {
  float x,y;
};

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

float distancia(struct Ponto p1, struct Ponto p2) {
  float d;

  d = sqrt(quadrado(p1.x - p2.x) + quadrado(p1.y - p2.y));
}

int main() {
  struct Ponto p1, p2;

  printf("Primeiro ponto:\n");
  printf("  x=");
  scanf("%f", &p1.x);
  printf("  y=");
  scanf("%f", &p1.y);

  printf("Segundo ponto:\n");
  printf("  x=");
  scanf("%f", &p2.x);
  printf("  y=");
  scanf("%f", &p2.y);

  printf("\nDistancia entre (%f, %f) e (%f, %f): %f\n", p1.x, p1.y, p2.x, p2.y,
          distancia(p1, p2));
}

Sua compilação e execução seguem abaixo:

> gcc -o distancia -lm distancia1.c  
> ./distancia
Primeiro ponto:
 x=1
 y=5
Segundo ponto:
 x=4
 y=9
Distancia entre (1.000000, 5.000000) e (4.000000, 9.000000): 5.000000

Observe que a compilação tem uma diferença em relação aos outros exemplos: a opção -lm passada ao compilador. Essa opção informa ao compilador que o programa precisa da bilioteca de funções matemáticas (chamada de libm, que vem de library math). Uma biblioteca é um arquivo que contém funções prontas para serem usadas. No exemplo acima, foi usada a função sqrt (abreviação de square root, ou raiz quadrada), que está implementada nessa biblioteca.

A função float distancia(struct Ponto p1, struct Ponto p2) calcula a distância entre os pontos dados pelos parâmetros p1 e p2. Esses parâmetros são do tipo struct Ponto, que foi definido pelo usuário da seguinte forma:

struct Ponto {
  float x,y;
};

O programa mostrado acima pode ser melhorado, para pareça mais simples e fácil de ser entendido:

#include <stdio.h>
#include <math.h>

struct Ponto {
  float x,y;
};

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

float distancia(struct Ponto p1, struct Ponto p2) {
  float d;

  d = sqrt(quadrado(p1.x - p2.x) + quadrado(p1.y - p2.y));
}

void le_ponto(char * mensagem, struct Ponto * p) {
  printf("%s\n", mensagem);
  printf("  x=");
  scanf("%f", &p->x);
  printf("  y=");
  scanf("%f", &p->y);
}

int main() {
  struct Ponto p1, p2;

  le_ponto("Primeiro ponto:", &p1);
  le_ponto("Segundo ponto:", &p2);

  printf("\nDistancia entre (%f, %f) e (%f, %f): %f\n", p1.x, p1.y, p2.x, p2.y,
          distancia(p1, p2));
}

A diferença em relação ao exemplo anterior está no aparecimento da função void le_ponto(char * mensagem, struct Ponto * p), que lê do teclado as coordenadas X e Y de um ponto. Essa função precisa fazer passagem de parâmetro por referência, uma vez que os campos x e y do argumento da função struct Ponto p devem ser modificados dentro da função, e essa modificação deve ser refletida fora da função. Veja que o acesso a esses campos tem uma sintaxe diferente:

  printf("  x=");
  scanf("%f", &p->x);

Sempre que um argumento do tipo struct for passado por referência (ex: struct Ponto * p), o acesso a seus campos deve usar essa sintaxe.