LINGUAGEM: Linguagem C - Capítulo 2 - Parte 1

Funções: definição, declaração e chamada

Funções:

Em C, um programa é uma coleção de FUNÇÕES. Uma função é um bloco de código que recebe um nome e que tem possivelmente PARÂMETROS. A partir do nome, o bloco de código que forma a função pode ser chamado. CHAMAR uma função significa iniciar a execução de um bloco de código. Caso a função possua parâmetros, durante a CHAMADA dela devem ser indicados ARGUMENTOS que são valores ou variáveis que irão corresponder aos parâmetros da função. Ao final da execução, uma função pode ainda retornar um valor para o código que fez a chamada da função.

Definição de Função: Forma Geral

Uma função deve ser definida conforme o esquema abaixo:
ESPEC-DECLARAÇÃO  ID( DECL-ID-1, ..., DECL-ID-n )  { 
 CORPO_FUNÇÃO 
}

ID define o nome da função. A lista de declarações DECL-ID-1, ..., DECL-ID-n é dita LISTA de PARÂMETROS da função. Cada ID-i consiste em uma variável com escopo local na função e que irá receber, como valor inicial, o valor passado como ARGUMENTO durante a chamada da função.

Se pensarmos no ID de uma função como uma VARIÁVEL GLOBAL, os ESPEC-DECLARAÇÃO definem que características tem este ID:
  • O tipo de dados ( void, int, char *, ... )
  • Classe de armazenamento ( static ou extern )
  • O tipo de acesso ( const ou volatile )

O CORPO_FUNÇÃO consiste em DECLARAÇÕES de VARIÁVEIS com escopo LOCAL seguidas de COMANDOS. Caso o tipo de dados constante nos ESPEC-DECLARAÇÃO seja diferente de void, deve haver ao menos um comando return ...; no CORPO_FUNÇÃO.

Definição de Função: Formato Antigo

Por questões de compatibilidade, uma função pode ainda ser definida seguindo o formato originalmente proposto pelos projetistas da linguagem. Neste formato original, o que muda é a forma de se especificar os parâmetros da função:
ESPEC-DECLARAÇÃO  ID( ID-1,...,ID-n )  
 DECL-ID-1; 
 ...; 
 DECL-ID-n;  
{ 
 CORPO_FUNÇÃO 
}

Declaração de Funções: Protótipos

O ideal é que toda função, antes de ser usada, deve ser declarada. Caso a função encontra-se DEFINIDA no mesmo arquivo e em uma posição antes de seu uso, então a própria DEFINIÇÃO da função serve como DECLARAÇÃO da função. Caso contrário, a DECLARAÇÃO da função dever ser feita através de PROTÓTIPOS. Um protótipo é como se fosse um CABEÇALHO para a função, ou seja, o ID da função os ESPEC do tipo retornado pelo função e os ESPEC dos tipos de cada parâmetro da função:
ESPEC-DECLARAÇÃO  ID( ESPEC-DECL-1, ..., ESPEC-DECL-n );

Em uma situação onde uma função f esteja sendo usada sem ter sido antes declarada, então implicitamente o compilador declarada a função como sendo:
int  f();

Um função f() é diferente de f(void) !

Um função declarada como
int  f();
é diferente de uma função declarada como
int  f(void);

No primeiro caso, o compilador não sabe se a função pode ou não receber argumentos durante a sua chamada. Quer dizer, é perfeitamente válido fazer:
int x = f(3.14);
 int y = f(2.7, 4);
 int z = f();

No segundo caso, o compilador é informado explicitamente que a função não deve receber nenhumar argumento. Assim, as duas primeiras linhas do código acima gerariam uma mensagem de erro e apenas a última seria considerada como válida.

Lista de Parâmetros Variável

Quando se quer especificar uma função que possui uma lista de parâmetros variável em número e tipo, deve-se terminar a declaração dos parâmetros utilizando três pontos ...

Por exemplo:
void f(int a, char * b, ... );

Qualquer função que use um número variável de parâmetros deve ter ao menos um deles explicitamente declarado.

va_arg(), va_start() e va_end()




Chamada de Função

Chamar uma função significa iniciar a execução de seu corpo de código. Caso a função tenha sido declarada com PARAMETROS, durante a chamada deve ser passados ARGUMENTOS que correspondam em número e tipo com os PARAMETROS da função.

PARAMETROS são variáveis locais à função que irão receber como valores iniciais os valores dos ARGUMENTOS utilizados na CHAMADA da função.



OBS.: Em C, as chamadas de função são feitas por VALOR. No entanto, o conceitos de chamada por referência pode ser simulado em C através do uso de apontadores.

Exemplo: CHAMADA por VALOR
unsigned modulo(int x) {
 x =  x < 0 ? - x : x ;

 return x;
}

int main () {
 int a = - 2, ma = modulo(a);

 printf("| %d | = %d\n",  a, ma);
}
Ao executar o programa acima, vc. irá perceber que o valor da variável a não é modificado pela função módulo. Ou seja, a variável a e usada como ARGUMENTO em uma CHAMADA por VALOR de modulo().

Exemplo: simulando CHAMADA por REFERÊNCIA
unsigned modulo(int * x) {
 *x =  *x < 0 ? - *x : *x ;

 return *x;
}

int main () {
 int a = - 2, ma = a;

 modulo(&ma);

 printf("| %d | = %d\n", a, ma);
}

Chamadas de Funções com ARRANJOS

O ID de vetores, matrizes e strings são conceitualmente apontadores para o primeiro elemento do arranjo. Assim, ao se usar o nome de vetores, matrizes ou strings em uma chamada de função está-se fazendo uma CHAMADA por REFERÊNCIA. Em outras palavras, vetores, matrizes ou strings quando passados como argumentos de funções e manipulados no interior dessas funções têm os seus valores modificados ao término da função (vide também: Matrizes e Parâmetros de Funções ).

Para evitar que modificações acidentais ocorram com vetores, matrizes e strings passados como argumentos de funções deve-se declará-los utilizando o qualificador const.

Exemplos:
/*   *str não deve ser modificado no interior de strlen 
*/
int strlen(const char str[]) {
 int i;
 
 /* str[0] = 'a';   - qualquer tentativa de modificar o conteúdo da string apontada 
    por str irá gerar uma mensagem de erro ! */

 for(i =0;  *str; i++, str++);
 
 return i;
}

/*     *str1 deve ser modificada no interior de strlen 
 já, *str2  não 
*/

int strcpy(char * str1, const char * str2) {
 int i = 0;
 
 /* str2[0] = 'a';   - qualquer tentativa de modificar o conteúdo da string apontada 
    por str2 irá gerar uma mensagem de erro ! */

 do
  str1[i] = str2[i];
 while ( str2[i++] );
 
 return i - 1;
}

Leituras Recomendadas

    • CCT cap 6
    • K&R

Exercícios

Obs.: Nos exercícios abaixo, escolha cinco e para estes mostre como as funções ficam quando definidas utilizando o formato antigo de definição de funções.

  1. Em C, qual a diferença entre DECLARAÇÃO e DEFINIÇÃO de funções?
  2. O que significam:
    1. passagem de argumentos por valor?
    2. passagem de argumentos por referência?
  3. Escreva uma função que recebe um número inteiro n>0 e devolve o número de dígitos de n e o primeiro dígito de n.
  4. Escreva uma função que recebe como parâmetro um inteiro positivo ano e devolve 1 se ano for bissexto, 0 em caso contrário. (Um ano é bissexto se (ano % 4 = = 0 && (ano % 100 != 0 || ano % 400 = = 0)).)
  5. Escreva uma função que tem como parâmetros de entrada e saída três números inteiros, dia, mes e ano, representando uma data, e modifica esses inteiros de forma que eles representem o dia seguinte.
  6. Escreva um programa que leia um inteiro positivo n e uma seqüência de n datas e imprime, para cada data, o dia seguinte.
  7. Escreva uma função de cabeçalho int divide (int *m, int *n, int d) que recebe três inteiros positivos como parâmetros e devolve 1 se d divide pelo menos um entre *m e *n, 0 caso contrário. Fora isso, se d divide *m, divide *m por d, e o mesmo para o *n.
  8. Escreva um programa que lê dois inteiros positivos m e n e calcula, usando a função acima, o mínimo múltiplo comum entre m e n, ou seja, mmc(m,n).
  9. Escreva uma função com protótipo void somabit (int b1, int b2, int *vaium, int *soma); que recebe três bits (inteiros entre 0 e 1) b1, b2 e *vaium e devolve um bit soma representando a soma dos três e o novo um bit "vai-um" em *vaium.
  10. Escreva um programa que leia dois números em binário e calcula um número em binário que é a soma dos dois números dados. Utilize a função acima.
  11. Escreva uma função com o protótipo void converte (char ch, int *tipo, char *valor); que recebe um caractere ch e devolve em *tipo 0, se o caractere for um número inteiro, 1 se for uma letra (maiúscula ou minúscula) e 2 caso contrário; e além disso, no caso de ser uma letra, converte para maiúscula, senão devolve ch inalterado.
  12. Escreva um programa que leia uma seqüência de n caracteres e imprima a seqüência convertida para maiúscula, eliminando os caracteres que não forem letras ou números.
  13. Escreva uma função que lê, linha a linha, uma matriz real Amxn
  14. Escreva uma função que imprime uma matriz real Amxn
  15. Escreva uma função que calcula a soma dos elementos da linha i de uma matriz real Amxn.
  16. Escreva uma função que calcula o produto dos elementos da coluna j de uma matriz real Amxn.
  17. Escreva uma função que troca o conteúdo de duas variáveis.
  18. Escreva uma função que recebe dois inteiros, i e j, uma matriz real Amxne troca linha i pela linha j. Utilize a função do item anterior.
  19. Um conjunto pode ser representado por um vetor da seguinte forma: V[0] é o tamanho do conjunto; V[1], V[2], etc. são os elementos do conjunto (sem repetições).
    1. Faça uma função chamada intersecção que dados dois conjuntos de números inteiros A e B, constrói um terceiro conjunto C que é a intersecção de A e B. Lembre-se de que em C[0] a sua função deve colocar o tamanho da intersecção.
    2. Faça um programa que lê um inteiro n > 1 e uma seqüência de n conjuntos de números inteiros (cada um com no máximo 100 elementos) e constrói e imprime um vetor INTER que representa a intersecção dos n conjuntos.
      • Por exemplo, se n=3 e os conjuntos são {1, 2, 4, 9}, {2, 4, 7, 8, 9} e {5, 4, 9}, a entrada será:
3  O valor de n
    
4  V[0] = tamanho do primeiro conjunto
1 2 4 9  V[1]  V[2]  V[3]  V[4]
    
5  V[0] = tamanho do segundo conjunto
2 4 7 8 9  V[1]  V[2]  V[3]  V[4]  V[5]
    
3  V[0] = tamanho do terceiro conjunto
5 4 9  V[1]  V[2]  V[3]
      • E o vetor INTER construído será
INTER[0] = 2     tamanho do conjunto
INTER[1] = 4  INTER[2] = 9  conjunto intersecção
      • NOTE que não é preciso ler todos os conjuntos de uma só vez. Você pode ler os dois primeiros conjuntos e calcular a primeira intersecção. Depois, leia o próximo conjunto e calcule uma nova intesecção entre esse conjunto lido e o conjunto da intersecção anterior, e assim por diante. Use obrigatoriamente a função do item anterior, mesmo que você não a tenha feito. 

Bibliografia e fonte:


  • [CCT] Schildt, H. (1996) C, completo e total: 3a Ed.. São Paulo, Makron.
  • LP, UFMA; Coutinho, Lucian. Linguagem de programação para ciencia da computação da ufma.http://www.deinf.ufma.br/~lrc/2009.1/LP/
  • [K&R] KERNIGHAN, B. e RITCHIE, D. (1990) C, a linguagem de programação: padrão ANSI. Rio de Janeiro: Campus.
  • DEITEL, H. M. (1999) Como programar em C. Rio de Janeiro: LTC.
  • Módulo Consultoria e Informática (1989) Linguagem C: programação e aplicações. Rio de Janeiro: LTC.