<aside>
Conhecimentos necessários:
Variáveis, entrada e saída de dados, estruturas condicionais e estruturas repetitivas
Dados, bases numéricas e memória
</aside>
<aside>
Navegação
</aside>
sub-rotinas, mais conhecida como funções, são blocos de podem ou não retornar um valor. Servem para quando um mesmo código será usado diversas vezes, fazendo um código ser reutilizado, além de ser uma porta para sistemas procedurais.
Para criar uma função, precisamos informar o tipo de dado, o nome, colocar parêntesis e chaves. Dentro das chaves irá o código, sendo obrigatório usar o “return” para retornar algo.
Ao retornar algum valor, a própria função passará a ter esse valor. Por exemplo: criei uma função que retorna 4, então eu posso usar essa função como uma variável que vale 4.
Dentro dos parêntesis vem os parâmetros, que são variáveis que criarmos para a função funcionar a base dela. Por exemplo: se eu quiser que uma função pegue o número inteiro que eu informar e multiplique por dois, eu posso criar um int nesses parâmetros para ela conseguir coletar esse int que irei informar e multiplicar por dois… caso tenha ficado meio confuso, tem alguns exemplos práticos abaixo.
Só mais uma coisa: toda função é criada fora e acima da função “int main()”.
Dito isso, considerando os dados básicos, teremos 5 tipos principais de funções:
Retorna, obrigatoriamente, um valor int. Um exemplo:
#include <stdio.h>
int funcao(){
return 2 + 2;
}
int main(){
printf("%d", funcao()); //vai mostrar 4
return 0;
}
Essa função está retornando 2 + 2, então é como que se ela fosse uma variável que vale 4, então por isso consigo usar ela como que se fosse uma dentro do printf.
Agora, irei criar uma função que fara o seguinte: vai coletar um número que eu informar e verificar se é ímpar ou par. Caso seja par, vai retornar o dobro desse número. Caso seja ímpar, vai retornar o triplo. Essa função ficaria assim:
#include <stdio.h>
int imparOuPar(int numInformado){
if(numInformado % 2 == 1)
return 3 * numInformado;
else
return 2 * numInformado;
}
int main(){
printf("Numero 6: %d.\\n", imparOuPar(6)); //vai mostrar 12
printf("Numero 11: %d.", imparOuPar(11)); //vai mostrar 33
return 0;
}
Tá vendo? Na hora de usar a função, eu informo o número, aí ele atribui automaticamente esse número para “numInformado” e faz as contas e verificações com ele.
Mesma lógica da função int, mas essa retorna um float. Um exemplo:
Irei fazer uma função que, se eu informar o método A, ela irá pegar um int e multiplicar por 2, mas se eu informar o método B, ela irá pegar um int e dividir por 2.
#include <stdio.h>
float doisMet(int num, char metodo){
if(metodo == 'A')
return 2 * num;
else if (metodo == 'B')
return num / 2.0;
else
return 0.0; //pois não foi informado o metodo A ou B
}
int main(){
float teste1 = doisMet(7, 'A'); //vai ser igual a 14
float teste2 = doisMet(7, 'B'); //vai ser igual a 3.5
float teste3 = doisMet(7, 'G'); //vai ser igual a 0;
printf("%f, %f, %f.", teste1, teste2, teste3);
return 0;
}
A saída desse código ficará: 14.000000, 3.500000, 0.000000.
Igualzinho à função float, mas com retorno double.
Mesma lógica das de cima, mas com um retorno char. Um exemplo:
Irei pegar uma função que coleta uma string e um int. Seu objetivo será retornar a letra que está localizada nessa posição.
#include <stdio.h>
char letra(int pos, char texto[]){
return texto[pos];
}
int main(){
int posicao = 0;
char frase[100];
printf("informe uma posicao: ");
scanf("%d", &posicao);
printf("escreva uma frase: ");
while (getchar() != '\\n'); //para limpar o buffer de entrada
fgets(frase, 100, stdin);
printf("A letra localizada da posicao %d eh: \\'%c\\'.", posicao, letra(posicao - 1, frase));
return 0;
}
Lembra que void é a ausência de variável? Então esse tipo de função é a única existente que não terá retorno, será literalmente apenas um bloco de código. Um exemplo:
Criarei uma função void que criará uma linha do tamanho que eu pedir.
#include <stdio.h>
void linha(int tamanho){
for (int i = 0; i < tamanho; i++)
printf("-");
printf("\\n"); //só para ir para a linha de baixo, quando acabar
}
int main(){
int tam = 0;
printf("informe o tamanho da linha: ");
scanf("%d", &tam);
printf("Exemplo de linha:\\n");
linha(tam);
printf("Exemplo de linha com o dobro de tamanho:\\n");
linha(2 * tam);
printf("Exemplo de linha com o triplo de tamanho:\\n");
linha(3 * tam);
return 0;
}
Bem mais prático assim, do que eu ter toda hora que escrever o for, né?
Outra coisa é que eu posso usar uma função dentro de outra função. Por exemplo, posso usar a função A dentro da função B, mas para isso, A tem que ser escrita primeiro. Um exemplo:
#include <stdio.h>
#include <string.h>
int acharPosicao(char txt[], char ltr){ //acha a posição da letra informada no texto
int i = 0;
while (txt[i] != ltr)
i++;
return i + 1;
}
void linha(int tamanho){
for (int i = 0; i < tamanho; i++)
printf("-");
printf("\\n"); //só para ir para a linha de baixo, quando acabar
}
void escreverTexto(char texto[], char letra){
linha(strlen(texto) + 37);
printf("Em: %s, o primeiro %c se localiza em: %d.\\n", texto, letra, acharPosicao(texto, letra));
linha(strlen(texto) + 37);
}
int main(){
escreverTexto("Errar eh humano.", 'h');
escreverTexto("Amanhecer o dia.", 'r');
escreverTexto("Domingo legal.", 'i');
return 0;
}
Viu como assim é bem mais prático? Separar em funções ajuda na organização e também para reaproveitar o código. Dessa forma, eu consigo usar “escreverTexto()”, se eu só quiser escrever uma linha com “linha()” ou ahar uma posição com “acharPosicao()”, eu ainda consigo.
Por curiosidade, a saída desse código fica assim:
-----------------------------------------------------
Em: Errar eh humano., o primeiro h se localiza em: 8.
-----------------------------------------------------
-----------------------------------------------------
Em: Amanhecer o dia., o primeiro r se localiza em: 9.
-----------------------------------------------------
---------------------------------------------------
Em: Domingo legal., o primeiro i se localiza em: 4.
---------------------------------------------------
Parâmetros são as variáveis que criamos na declaração das funções. Argumentos são os valores que passamos para essas variáveis. Pegando um dos exemplos acima, seria assim:
#include <stdio.h>
void linha(int tamanho){ //aqui eu criei um parâmetro
for (int i = 0; i < tamanho; i++)
printf("-");
printf("\\n");
}
int main(){
int tam = 0;
printf("informe o tamanho da linha: ");
scanf("%d", &tam);
printf("Exemplo de linha:\\n");
linha(tam); //aqui eu passei um argumento
printf("Exemplo de linha com o dobro de tamanho:\\n");
linha(2 * tam); //aqui eu passei outro argumento
printf("Exemplo de linha com o triplo de tamanho:\\n");
linha(3 * tam); //aqui eu passei mais um argumento
return 0;
}
As variáveis podem ser divididas entre locais e globais, de forma que as locais são as variáveis escritas dentro de alguma função ou nos seus parâmetros sejam as locais e as variáveis escritas foras das funções são globais.
As variáveis locais não podem ser acessadas por outras funções, e, por isso, até podem existir duas variáveis locais com o mesmo nome, mas em funções diferentes.
Já as variáveis globais podem ser acessadas por todas as funções e, por isso, não pode haver outras variáveis com o mesmo nome. Um exemplo:
#include <stdio.h>
#include <string.h>
int global = 123; //essa variável é global e qualquer função consegue acessar ou modificar ela
void escreverNum (int local){ //essa varíavel é local, ela só existe dentro dessa função
printf("%d\\n", local);
}
int numero (int local, char texto[]){ //essas duas variáveis também são locais
return global + local + strlen(texto);
}
int main(){
int local = 12; //essa variável também é local e só existe dentro da função main
char texto[] = "Tao natural quanto a luz do dia."; //outra variável local
escreverNum(local); //exibe 12
printf("%d\\n", numero(local, texto)); //exibe 167
global = 0;
printf("%d", numero(local, texto)); //exibe 44, porque o int global agora é 0
return 0;
}
Obs.: Se dentro de uma função você criar uma variável local com o mesmo nome de uma variável global, dentro dessa função só funcionará a variável local.
São funções que chamam a si mesmas. Um exemplo:
#include <stdio.h>
int fatorial(int n) {
if (n == 0) return 1; //Se for zero, retorna um e acaba a recursividade
return n * fatorial(n - 1); //chama a função recursivamente
}
int main() {
printf("Fatorial de 5: %d.", fatorial(5)); //exibe 120
return 0;
}
Um exemplo de função que faz o papel de calcular fatorial.
#include <stdio.h>
void fibonacci(int elementoAnterior, int elementoAtual, int posElemento, int tamanho){
if(posElemento != tamanho){
printf("%d ", elementoAtual);
fibonacci(elementoAtual, elementoAnterior + elementoAtual, ++posElemento, tamanho);
}
}
int main() {
fibonacci(0, 1, 0, 10); //exibe a sequência de Fibonacci com 10 elementos
return 0;
}
Outro exemplo seria essa função que se chama até escrever a sequência de Fibonacci com a quantidade de elementos pedida.
Com a criação de variáveis, podemos criar uma e dps atribuir um valor, né? Tipo assim:
int idade;
idade = 18;
Podemos fazer a mesma coisa para as funções! Podemos criar uma e dps dar os códigos dentro! Um exemplo:
#include <stdio.h>
void linha(int);
int main(){
linha(12);
linha(21);
return 0;
}
void linha(int tamanho){
for (int i = 0; i < tamanho; i++)
printf("-");
printf("\\n"); //só para ir para a linha de baixo, quando acabar
}
Eu criei a função e coloquei um int dentro dela, mas não é necessário colocar o nome. Logo após, encerrei com ponto e vírgula. Os comandos só são colocados depois da função main(). Como a função “linha()” foi declarada antes da main(), tudo funcionará corretamente, mesmo só sendo definida no final do código.
Se iremos usar alguma função com bastante frequência, você pode transformar ela em uma biblioteca sua! Para isso, você precisa criar um arquivo com o nome da biblioteca. Por exemplo: quero uma biblioteca com o nome de “jplib.h” (o “.h” é obrigatório e não coloque acento no nome), então irei criar um arquivo com esse nome na pasta do projeto do compilador e colocar esse código:
#ifndef JPLIB_H
#define JPLIB_H
//as declarações das funções vem aqui dentro
#endif
Após isso, irei criar um arquivo com o mesmo nome, mas sendo “.c”, ou seja, “jplib.c”. Nele eu irei colocar escrever #include “jplib.h” (com aspas, mesmo) e colocar as definições das funções. Assim, crio a biblioteca “jplib.h” que posso importar no meu arquivo principal do projeto (provavelmente ele se chama “main.c”. Vou dar um exemplo de tudo isso:
Arquivo: jplib.h
#ifndef JPLIB_H
#define JPLIB_H
void linha(int);
#endif
Arquivo: jplib.c
#include <stdio.h>
#include "jplib.h"
void linha(int tamanho){
for (int i = 0; i < tamanho; i++)
printf("-");
printf("\\n"); //só para ir para a linha de baixo, quando acabar
}
Arquivo: main.c
#include <stdio.h>
#include "jplib.h"
int main(){
linha(12);
linha(21);
return 0;
}
Assim, crio uma biblioteca que posso usar!
Na parte do:
#ifndef JPLIB_H
#define JPLIB_H
Você pode colocar qualquer nome, contanto que os dois sejam iguais. Por exemplo:
#ifndef ZEZINHO
#define ZEZINHO
Assim funcionaria, mas é sempre recomendado, para facilitar a leitura do código, você escrever o nome da sua biblioteca e colocar um “_H” no final.
Essas linhas de código servem para evitar problemas, caso a pessoa importe a biblioteca duas vezes seguidas.
Obs.: não esqueça de ativar para o compilador compilar todos os arquivos! Se possível, crie cada um dos arquivos no próprio compilador.
Mas, e se quisermos criar uma função que modifica o valor de alguma variável? Tipo uma função que pega uma string qualquer e remove todos os espaços? É aí que usamos as funções com argumentos por referência!
#include <stdio.h>
void removerEspacos(char str[]) {
int j = 0;
for (int i = 0; str[i] != '\\0'; i++) {
if (str[i] != ' ') {
str[j++] = str[i];
}
}
str[j] = '\\0';
}
Observe essa função, ela está removendo os espaços da string str, mas isso não leva a lugar nenhum! Como é uma variável local, essa string não servirá de nada. Para resolver isso, podemos fazer duas coisas: ou retornar uma string, ou usar as funções com argumentos por referência… porém ambas necessitam de conhecimentos em arrays e ponteiros… então irei iniciar brevemente a segunda opção aqui!
Para fazermos essa função mudar de fato a string que colocamos nela, basta colocarmos um asterisco antes do nome da string e tirar os colchetes, desse jeito aqui:
#include <stdio.h>
void removerEspacos(char *str) {
int j = 0;
for (int i = 0; str[i] != '\\0'; i++) {
if (str[i] != ' ') {
str[j++] = str[i];
}
}
str[j] = '\\0';
}
Prontinho! Agora ela muda o valor da string original também. Um exemplo aqui:
#include <stdio.h>
void removerEspacos(char *str) {
int j = 0;
for (int i = 0; str[i] != '\\0'; i++) {
if (str[i] != ' ') {
str[j++] = str[i];
}
}
str[j] = '\\0';
}
int main(){
char string[] = "Teste De String";
removerEspacos(string);
printf("String final: %s", string); //exibe "TesteDeString"
return 0;
}
Isso também funciona para outros tipos de variáveis, mas cuidado que muda um pequeno detalhe… Vou te mostrar em uma função que pega uma variável int qualquer e muda para 0.
#include <stdio.h>
#include <stdio.h>
void tozero(int *num){
*num = 0;
}
int main(){
int idade = 18;
tozero(&idade);
printf("Int final: %d", idade);
return 0;
}
Viu algumas diferenças? A primeira é que não fiz num = 0;, mas sim *num = 0;. A segunda é que não passei apenas a variável, mas coloquei um “&” antes do nome dela. No momento, não posso ensinar ou explicar muito sem ensinar sobre ponteiros primeiro, então pense no seguinte:
É como no scanf: a string não precisa do “&”, nem do asterisco, enquanto as outras variáveis precisa, de forma que: usamos o asterisco para igualar a algum valor dentro da função e o “&” para passar a função nos argumentos.