Linux Tech Hacks

Your Linux tech hacks and Tips

Programação em C (por Antonio "Cooler_")

ATENÇÂO ESTE PAPER é BETA, ainda está em escritá
-------------------------------------------------------------------------------------------
INICIANDO em
 ____     __                                                     _________
|    |   |__| ____    ____  __ _______     ____   ____   _____   \_   ___ \
|    |   |  |/    \  / ___\|  |  \__  \   / ___\ / __ \ /     \  /    \  \/
|    |___|  |   |  \/ /_/  |  |  // __ \_/ /_/  X  ___/|  Y Y  \ \     \____
|_______ \__|___|  /\___  /|____/(____  /\___  / \___  |__|_|  /  \______  /
        \/       \//_____/            \//_____/      \/      \/          \/

             |##|
             )  (
             (  )
            _)  (_
           |      |      ,           ,
           |------|     /     GNU     \
           | GCC  |    ((__-^^-,-^^-__))     Autor: Antonio "Cooler_"
   |~~~~~| |VODKA |     `-_---' `---_-'      Contato: c00f3r@gmail.com
   `--,--' |------|      `--|o` 'o|--' -----------+
      |    |      |         \  `  /                \-\\
    __|__  |______|          ): :(    \      /  /   \ \\
                             :o_o:    /      \     / | \\
                              "-" \     ------\   / /
                                   |/ \|       |/ \|
                                   |   |       |   |
                                  ()  ()      ()  ()

Introdução a linguagem C
--------------------------

Linguagem C foi inventada em 1970 por Dennis Ritche, teve grande influência
do seu amigo Ken Thompson que criou a linguagem "B",ambos trabalharam na AT&T
Bell Labs,está foi uma época bem interessante Dennis Ritche e Ken thompson
fizeram um Unix em C,já tinham um Unix com assembly e tal porem não era portável,
se não me engano foi o "sistem V" o primeiro Unix a usar C,em fim nesta época
erá duro fazer um programa portável com Assembly.Em 1978, Brian Kernighan(que é
um dos criadores da linguagem AWK) e Dennis Ritchie publicaram a primeira edição
do livro The C Programming Language.Esse livro, conhecido pelos programadores
de C, como "K&R", serviu durante muitos anos como uma especificação informal da
linguagem. A versão da linguagem C que ele descreve é usualmente referida como
"K&R C".

Curiosidades:

*Em 1983, Dennis Ritche e Ken Thompson receberam o prémio Turing "pelo seu desenvol-
vimento de teoria de sistemas operativos genéricos e especialmente pela sua
implementação do sistema operativo UNIX."
A denominação do prêmio é homenagem a Alan Mathison Turing, um matemático britânico
considerado um dos pais da ciência da computação moderna. O prêmio é conhecido como
o "Prêmio Nobel da computação". É patrocinado pela Intel Corporation e acompanhado
atualmente por um prêmio monetário no valor de 250.000,00 USD (duzentos e cinquenta
mil dolares americanos).

*Também receberam premio nacional de tecnologia pelas mãos do presidente Bill Cliton

*Dennis Ritche Nascido em Bronxville, Nova Iorque, Ritchie formou-se em física e
matemática aplicada pela Universidade de Harvard.

*Ken thompson criou a codificação de caracteres UTF-8,construiu programas para jogar
xadrez contra o campeão do ramo Kasparov.

*Tanto Ken Thompson como Dennis Ritche tem um projeto com Rob Pike de sistema
Operacional chamado Plan9 e um sistema operacional hospedeiro chamado inferno
ambos são livres

*Em 1980, Bjarne Stroustrup do mesmo laboratório Bell Labs criou o C++ que seria
um "C + smalltalk" ou seja linguagem C com classes,C orientado a objetos,fui meio
infeliz nesta comparação que fiz, mesmo por que C++ é diferente de C ansi em vários
aspectos não vou discutilos aqui...

algumas refêrencias rápidas:

 http://plan9.bell-labs.com/who/dmr/
 http://plan9.bell-labs.com/who/ken/
 http://c-faq.com/ansi/index.html

Separando o material
--------------------------

O paper teve início com história da linguagem pois isso é muito importante
dá um mundo para o que vamos estudar,o que adianta estudar algo sem saber
de onde veio as cegas ? durante muito tempo eu aprendi as cegas na faculdade,
não desejo isso para nimguém...

em fim para continuar a ler este paper você deverá separar o seu "Tool Box"
para  trabalhar com linguagem C,primeira coisa é usar um bom editor como VIM,
EMACS,gedit caso não queira nenhum desses tem o jedit entre outros,segundo
item seria ter um compilador eu pessoalmente uso o GCC sugiro o mesmo.

exemplo de instalação do compilador,editor de texto,desbuger em um Linux
baseado em GNU Debian...
 apt-get install gcc g++ make gdb vim

exemplo em Linux baseado em Redhat
 yum install gcc g++ make gdb vim

e assim vai gentoo use merge,arch pacman,slack installpkg etc...
caso use algum *BSD,MacOS ou OpenSolaris procure nos Ports dos mesmos

 http://www.macports.org/
 http://www.freebsd.org/ports/
 http://gcc.gnu.org/

Maioriados Unix Like vem com GCC como default,a não ser que seja
minimalista de mais...

Caso você use windows gcc tem uma versão para Windows que se chama MinGW.
É a mesma instalada pelo ambiente Dev-C++ ou pelo CodeBlocks.

 http://www.mingw.org/
 http://www.codeblocks.org/

Quanto ao editor tendo um highlights(letras coloridas quando você
está editando o código) já da para quebrar o galho eu mesmo prefiro
o "VIM". terceiro ferramenta um desbugador,depurador o GDB já resolvi
isso,lembrando que o CodeBlocks já tem editor e debuger se não me engano...

instalado o GCC caso use Unix Like como Linux,BSD etc,de o comando
 man gcc

assim você vai ver o manual do GCC caso use windows não fique triste
veja nos link

 http://gcc.gnu.org/onlinedocs/gcc-4.5.0/gcc/
 http://gcc.gnu.org/onlinedocs/gcc-4.5.0/gcc.pdf

não é para ler tudo de cara, mas deixe numa gaveta como referencia futura...
se você poder ter o livro do K&R ajuda também,outro bom livro como "C primer plus"
até mesmo "practical C". Estudar códigos de outros programadores ajuda seu
conhecimento na linguagem a subir exponencialmente.


O que é um Compilador
-------------------------

Sendo curto e direto ao Ponto,Compilador ele pega o código escrito de alto nível
por um editor qualquer,compila através de automatos,geradores léxicos,condições
para execuções de certas funções para gerar o código de máquina...

se o scaner léxico acusar algo fora do padrão da linguagem então é retornada
uma saída de erro "syntax error before x bla bla..."

por exemplo uma linguagem X de programação, usa função "escreva()" para mostrar
uma saída,e todo fim de uma função nesta nossa linguagem tem que ter "string"
de ";"(ponto e virgula),exemplo prático:

 escreva(ola mundo)

compilador vai ler letra por letra

 [e][s][c][r][e][v][a][(][*][)][][][]...--> campos com espaços nulos
  \  |  |  |  |  |  /           |
    se for (escreva)            verifica se tem ";"
     mostre o que               se não tiver retorna com erro
   tem entre parentes

ao ver que o ultimo campo não tem ";" para finalizar vai retornar erro de
sintaxe, e irá parar tudo,caso a sintaxe esteja OK o compilador vai continuar
com sua tarefa,em outras palavras veja o desenho:

 _______                                           ________
|       |                                         |01010101|
|code   |                                         |01010101|
|       |------->> Compilador compila então --->> |10101010| <<---- Executa
| Ola!  |                                         |01010101|       retorna saida
+-------+                                         +--------+         "Ola!"

Compilador pega código de alto nível e passa para baixo nível,ou seja nível
de máquina,tem muitas paradigmas das quais poderia falar aqui mas não é meu
objetivo explicar o grande paradoxo que tem dentro dos compiladores.

Se o programa faz referência a funcoes que estejam definidas em outros
arquivos objetos (como as bibliotecas padroes libc e glibc) entao o linka-
dor une tudo fazendo um executavel! Nosso programa está pronto.

caso queira saber mais os internals sugiro ler o "Dragon Book"
"Compilers: Principles, Techniques and Tools", 1986
tem este apelido por ter um dragão na capa, eu tenho este livro e acredite
eu li todo, infelizmente como não pratiquei todo o conteudo não fixou no meu
cérebro,eu estudei mais para ficar bom em parsers...

 http://dragonbook.stanford.edu/

se você não saber resolver algoritmos de árvores,grafos nem perde seu tempo
lendo hehehe.


Iníciando em C
------------------------------------

Bom Primeira paradigma que devemos ter é que os códigos não vem do além,cada
função em linguagem C vem de uma "header","API" ou popularmente "biblioteca".
Primeira "Header" que vamos usar é a "stdio.h" que tem conformidade com ANSI
X3.159-1989 (‘‘ANSI C’’),siglas da "header" seria standard input/output library
traduzindo ai "biblioteca padrão de entrada e saida".Isso em um sistema Unix Like
você resolveria com comando "man stdio",caso não use um sistema Unix like ai
está alguns manuais.

 http://www.manpagez.com/man/3/stdio/ man em EN
 http://man.gnusquad.org/stdio/section-3/pt/ man em PT

Com esta "header" que vamos pegar entrada do usuário com nossos programas usando
função "scanf",saída vamos usar a função "printf".

vamos a um "hello world" parece que todo paper de programação tem isso he he

---------------------------code

int main() {
 puts("ola\n");
 return 0;
}

---------------------------EOF

escreva o código num paper txt, e renomeio para extensão ".c"
veja seguencia de comando que mandei no terminal

 vim programa.c   <---usei o vim para escrever o código no arquivo "programa.c"
 gcc programa.c -o programa <--- compilei com GCC e usei argumento "-o" para apontar a saida
 ./programa

teve como saida a palavra "ola"
a função "puts()" não vem de uma "header" repare que não apontamos para nenhuma
ou seja não usamos "stdio.h".caso use windows use uma "IDE" como dev C++ ou codeblocks
nos menus dele tem opção de compilar e executar,"\n" dentro dos parenteses seria
comando "new line" par apular uma linha depois da "string".

a função "main()" é uma função padrão da linguagem C que sempre é a primeira
função a ser executada pelo interpretador,geralmente toda função em C tem que
retornar um valor então justifica o "return 0" no final do código,é possível
a função retornar nada com "void" mas isso é outra história...

Nosso exemplo foi muito minimalista,vamos trabalhar com saídas só que usando
a função "printf()" e proveitando para explicar variáveis,expressões aritméticas e
funções.Bom agora sim vamos usar "stdio.h" para apontar para ela vamos usar

 #include
 ou
 #include "stdio.h"

vamos a um exemplo

---------------------------code

#include

/* isso é um comentario */

int main() {

//outra forma de comentar
// veja função "printf"

 printf("ola\n");

 return 0;
}

---------------------------EOF

O pre-processador remove comentários do programa que seria iniciados com "//" ou
"/* conteudo */",interpreta diretivas especiais do pre-processador. Estas
diretivas sao iniciadas por # que pode ser um apontador para header ou macros
exemplo de macro

diretiva #define que eh assim:

#define MAX_SIZE 100

Faz uma substituicao completa por todo arquivo trocando MAX por 100.

---------------------------code

#include
#define MAX 100

int main() {
 printf("numero %d \n", MAX);
 return 0;
}

---------------------------EOF

Explanação de o que ocorreu dentro da função "printf()"

                      pula
                      linha   número inteiro
                        |     /
printf("string     %d  \n",  21);
           |        |
          texto     \
         qualquer  argumento que define
                   tipo de variável
                   no caso número inteiro
                   integer "int"
                   então usamos "%d"

Só pro curiosidade algumas sequencias de escape:

\a (alert) -> Produz um alerta visivel ou audivel.
\b (backspace) -> Muda a posicao atual 1 caractere antes.
\f (form feed) -> Move a posicao atual para o inicio da proxima pagina.
\n (new line) -> Move a posicao atual p/ posicao inicial da proxima linha.
\r (carriage return) -> Move a posicao atual p/ a posicao inicial da linha.
\t (horizontal tab) -> Move a posicao atual p/ a proxima posicao de tabulacao definida na linha.
\v (vertical tab) -> Move a posicao atual p/ a proxima posicao de tabulacao vertical definida.



int - interger - inteiro
---------------------------
variavel inteira é aquela que assume valores inteiros.
O tipo basico para um inteiro é "int", ou seja, declaramos um inteiro
assim:

 int x;

 ou

 long int x;

O valor de um inteiro assim declarado é um inteiro de 32bits ou seja,
pode ir de -2147483648 a 2147483647. Inteiros de 16 bits ainda podem ser
declarados, bastando anexar a palavra short antes de int:

 short int x;

 ou

 short x;

Isso faz com que x varie de -32768 a 32767. Um inteiro pode ser
declarado para ser positivo apenas, bastando colocar a palavra
unsigned (sem sinal), então temos:

 unsigned shor int x;

seria uma declaracao no qual os valores de x podem ir de 0 a 65535 (2^16-1)..

 unsigned long int z;

seria uma declaracao no qual os valores de z podem ir de 0 a 4294967297 (2^32 - 1).
Segue aqui uma tabela com os principais tipos de declaradores para inteiros:

+---------------------+--------------------+
|       16 bit        |       32 bits      |
|---------------------|--------------------|
| short               | long               |
| short int           | long int           |
| unsigned short int  | unsigned long int  |
| signed short int    | signed long int    |
|                     | unsigned int       |
|                     | signed int         |
+---------------------+--------------------+

Os operadores aritmeticos servem para realizar operacoes aritmeticas, ou
seja, aquelas que envolvem as operacoes basicas que aprendemos logo nos
primeiros anos da escola: soma, subtracao, multiplicacao e divisao. As
expressoes sao do tipo (membro1 operador membro2) e o valor retornado pela
expressao eh exatamente o resultado da operacao.vamos a um exemplo usando
expressões aritméticas conforme a tabela.

+---------------------------------------------------------------------------+
|  Adição   |  Subtração   |  Multiplicacao  |  Divisão  | Resto de Divisão |
|--------------------------------------------------------|------------------|
| (a + b)   |   (a - b)    |     (a * b)     |   (a / b) |    ( A % B )     |
+--------------------------------------------'-----------'------------------+

exemplo apenas atribuição de valores em variáveis
---------------------------code

#include

int main() {
//declaramos variáveis
 int x,y,z;
//setamos valor nas váriaveis
 x=2,y=3,z=5;
 printf("escolhidos %d %d %d \n", x, ,y ,z);
 return 0;
}

---------------------------EOF
repare que usamos "," virgula para separar a atribuição das variáveis
podiamos usar também;
 x=2;
 y=3;
 z=5;

vamos exemplo com atribuição e uso de operadores aritméticos

---------------------------code

#include

int main() {
//declaramos variáveis
 int x,y,z;
//setamos valor nas váriaveis
 x=2+2;
 y=3-2;
 z=5*2/2;
 printf("resultado de x,y e z é %d %d %d \n", x, y, z);
 return 0;
}

---------------------------EOF

Podemos atribuir uma váriavel dentro da outra desde que seja do mesmo tipo
exemplo

---------------------------code

#include

int main() {
//declaramos variáveis
 int x,y,z;
//setamos valor nas váriaveis
 x=2;
 y=3;
 z=x+y;
 printf("resultado de z é  %d \n", z);
 return 0;
}

---------------------------EOF

podemos usar notação pósfixa para atribuir um valor exemplo invés de
fazer
 x=x+1;
 podemos fazer
 x+=1;
 ou x++
para atribuição de valor "1" podemos fazer até x++;
vou dar um exemplo prático e pesado para estudo, tente entender o que
cada linha faz...

---------------------------code

#include

int main() {
//declaramos variáveis
 int x,y,z;
//setamos valor nas váriaveis
 x=2;
 y=3;
 z=5;
 z+=(x+y); // isso vale como z=z+(x+y)
 printf("resultado 1 de z é  %d \n", z);
 z++; // equivale a z=z+1
 z++;
 printf("resultado 2 de z é  %d \n", z);
 z*=2; // z=z*z
 printf("resultado 3 de z é  %d \n", z);
 z%=12;
 printf("resultado 4 do resto da divisao de 24 por 12 de z é  %d \n", z);
 z=13;  
 z+=x++ + ++y; // ou seja z=z+(x+1)+(y-1)
 printf("resultado 5 de z é  %d \n", z);
 z-=1; // valia 19 agora vale 18
 z-=12 + x++; // ou seja z=z-(12+(x+1))
 printf("resultado 6 de z é  %d \n", z);
 z=20; //agora z vale 20
 z+=y-- + --x; // z=z+(y-1)+(-x)
 printf("resultado 7 de z é  %d \n", z);
 z=20; //z agora vale 20
 z+=(((20/5)+(1-1))*3)/x; // isso vale z=z+( (((20/5)+(1-1))*3) /x )
 printf("resultado 7 de z é  %d \n", z);

 return 0;


---------------------------EOF

acho que com este ultimo exemplo deu para explanar operadores aritméticos
com variáveis inteiras,agora mudando de assunto porem dentro do tópico "int" 

Em resumo os tipos de declarações de "int"

 * int, pode possuir 16 bits, 32 bits ou 64 bits
 * short int, deve possuir tamanho de no mínimo 16 bits e não pode ser maior que int
 * long int, deve possuir tamanho mínimo de 32 bits
 * long long int, deve possuir tamanho mínimo de 64 bits

Todos estes tipos de inteiros podem ainda ser declarados precedidos da cláusula unsigned,
o que faz com que só suporte números positivos. Isto faz com que, com o mesmo tamanho, uma
 variável suporte mais números positivos do que um signed (todos os inteiros são signed por
 omissão).

como eu sei quantidade de Bits de uma variável ?

usamos função "sizeof()" para determinar o tamanho de uma variavel, tipo ou
constante. O valor retornado é em bytes.
---------------------------code

#include

int main() {
 long int x;
 x = sizeof(x);
 printf("bytes de x é  %d , tem total de %d bits\n", x, x*8);

---------------------------EOF
Acima, o valor de x será 4, ou 4 bytes, que dah 32bits, ou seja, x é um
inteiro de 32 bits.

antes de partir para float só mais um exemplo,desta ves um programa
que mostra alguns dos argumentos na função "printf()" são eles "%d"
que já usamos que seria para decimal,"%o" para octal, "%x" hexadecimal
---------------------------code

#include
int main(void)
{
   int x = 100;
   printf("dec = %d; octal = %o; hex = %x\n", x, x, x);
   return 0;
}

---------------------------EOF
rode o programa e tente entender...


float
------------
Uma variavel racional,real(ponto flutuante) é aquela que assume valores
racionais.

Podemos declarar assim:

float x;

ou

double y2;

Na primeira declaracao, temos x um ponto flutuante de precisao simples,
e xy um ponto flutuante de precisao dupla, ou seja, x pode ter
mais casas decimais do que y. Voce tambem pode usar unsigned, long e
short para float:

unsigned float z;

+----------------+-----------------+
| float          | double          |
| short float    | short double    |
| long float     | long double     |
| unsigned float | unsigned double |
| signed float   | signed double   |
+----------------+-----------------+

vamos a um exemplo de uso em um probleminha de porcentagem
Um produto, cujo valor original era de R$ 250,00, teve um desconto de 10%.
Qual foi seu valor final da compra?

---------------------------code

#include

int main() {
  float valor,percentual,total;
 
  valor = 250.00;
  percentual = 10.0 / 100.0;
  total = valor - (percentual * valor);
  printf("O valor final é %.2f \n", total);

  return 0;
}

---------------------------EOF

Só uma explicação rápida do que ocorreu na função "printf()"

  printf("O valor final é %.2f \n", total);
                |           |
              string        |_usamos argumento "%f" para float
             qualquer         ".2" seria para indentificar
                               quantos zeros antes do "ponto"
                               se você usar "%.3f" então vai
                               ter "225.000" invés de "225.00"

Mistério Descobrido !
Scooby Scooby Doooooo AhUHAuHAHAU !


Char - caracter - alfanumérico
---------------------------------
O tipo char ocupa 1 byte, e serve para armazenar caracteres ou inteiros.
Isso significa que o programa reserva um espaço de 8 bits na memória RAM
ou em registradores do processador para armazenar um valor (char de tamanho
maior que 8 bits é permitido pela linguagem, mas os casos são raros). Com
vetores do tipo char é possível criar cadeias de caracteres (strings).

Um caractere em ASCII - American Standart Code for Information
Interchange, tem um valor que varia de 0 a 255. Ou seja, quando voce declara
um caractere, voce estah na verdade declarando um inteiro de 0 a 255.

char letra;

É importante voce saber boa parte da tabela ASCII, principalmente se voce
quer lidar com baixo nivel. O seguinte programa em C exibe a tabela
para voce:

---------------------------Code
#include

int main() {
 unsigned char i;
 for (i=0;i<255;++i)
// %c argumento para definir apenas um char
  printf("%d -> %c\n",i,i);

 return 0;
}
---------------------------EOF

Por exemplo, a letra A (maiusculo) tem o valor 65. Assim, apos
declarado a variavel

 unsigned char c;

voce poderia ter feito..

 c = 'A';

ou

 c = 65;

pois o resultado seria o mesmo.
Strings ficam interessantes mesmo com vetores "arrays",não é legal eu
falar disso agora,Mas vou tentar,um "Array" nada mais é que uma lista
em linguagem C podemos definir por "[]" e dentro vai o número da quantidades
de elementos do "Array",por exemplo "[4]" então é um "array" de 5 elementos

 [0][1][2][3][4]  isso por que contamos apartir do ZERO

vamos um exemplo prático
---------------------------Code
#include
#define frase "olhe que legal estou vivo!"
int main() {
  char name[10];

  printf("qual seu nome?\n");
  scanf("%s", name);
  printf("Ola, %s. %s\n", name, frase);
  return 0;
}
---------------------------EOF

%s = seria o argumento para definir um conjunto de caracteres
usamos uma função nova até aqui o "scanf()" que serve para pegar
entradas "stdin" ou seja entradas do teclado.programa mostra como
saida "qual seu nome?" então o cliente vai digita o nome,então
o programa retorna a saída "ola Fulano , olhe que legal"...

só para discutir aqui, está forma de pegar string de entrada não
é a melhor forma, memso pro que alguns compiladores podem retornar
algum BUG,veja o por que

array name[15]
[c][h][u][c][k][][n][o][r][r][i][s][\0][][]
|                                    |-> null caracter
|-usuário digita nome dele chuck norris

o problema é que alguns compiladores faz

array name[15]
[c][h][u][c][k][][n][o][r][r][i][s][\0][\n][] e "\n" na próxima entrada
|                                    |-> null caracter
|-usuário digita nome dele chuck norris

isso acaba bugando seu programa então tem que fazer uma gambiarra
usando "getchar()"

scanf("%s", ola); getchar();

assim pegando \0\n que tem a mais...

só uma curiosidade se você quiser mostrar somente uma letra da palavra
pasta mostrar sua posição exemplo,"chuck norris" se pegarmos o terceiro
caracter "u", então teriamos
[c][h][u][c][k]....
 0  1  2  3  4 <--- número da posição dos elementos do "array"

então
 printf("%c", var[2]);

isso não é nada elegante,então baseada na idéia de um amigo o "m0nad" fiz
uma macro para resolver o problema, na versão do m0nad seria uma função chamada
"chomp" com influências na linguagem interpretada Perl.
---------------------------Code

#include
#include

#define ReadString(a) fgets(a,sizeof(a),stdin),a[strlen(a)-1] = '\0';

int main() {
 char teste[10];

 printf("seu nome\n");
 ReadString(teste);
 printf("voce escreveu %s\n",teste);

 return 0;
}

---------------------------EOF

vamos lapidar a nossa macro "ReadString"
 fgets(var,sizeof(var),stdin); <-- usamos para ler a entrada stdin
 var[strlen(var)-1] = '\0'; <-- usamos para deletar "\0" e assim não bugar

a função "strlen()" esta inclusa na header "string.h" isso justifica o uso
da header,lembrando que nesta header tem muitas funções legais de strings
como exemplo você deseja concatenar strings. usamos função "strcat"

---------------------------Code
#include
#include
#define ReadString(a) fgets(a,sizeof(a),stdin),a[strlen(a)-1] = '\0';

int main() {
 char animal[10], animal2[10], soma[30];

 printf("nome de um animal \n");
 ReadString(animal);
 printf("nome de um animal \n");
 ReadString(animal2);
 strcat(soma,animal); //soma+=animal...
 strcat(soma," e o animal ");
 strcat(soma,animal2);
 printf("string concatenada %s \n", soma);
 return 0;
}
---------------------------EOF

caso use um "unix like" de o comando num terminal "man string" para ver o manual
da "string.h" caso não visite
 http://www.manpagez.com/man/3/string/


Comente com o Facebook:

Nenhum comentário:

Postar um comentário

Siga-nos