├── src
├── imgs
│ ├── c.png
│ ├── TiposPrimitivos.png
│ └── recursao_drawing_hands_escher.png
├── 4-Ordenacao
│ ├── Exercicios.pdf
│ ├── Bubble Sort.c
│ ├── Selection Sort.c
│ ├── Quick Sort.c
│ └── Merge Sort.c
├── 1-Introducao
│ ├── 2-Comunicacao.md
│ └── 1-Boas-vindas.md
├── 2-Ambiente
│ ├── 1-Editores-e-plugins.md
│ ├── 5-Ambiente-online.md
│ ├── 3-Ambiente-linux.md
│ ├── 4-Dicas-gerais.md
│ └── 2-Ambiente-windows.md
├── 3-Basico
│ ├── 04-Constantes.md
│ ├── 11-Matrizes.md
│ ├── 08-Operadores-Logicos.md
│ ├── 01-PrimeirosPassos.md
│ ├── 06-Funcoes.md
│ ├── 10-EstruturasDeRepeticao.md
│ ├── 07-Arrays.md
│ ├── 13-MemóriaDinâmica.md
│ ├── 03-Entrada-Saida.md
│ ├── 14-Arquivos.md
│ ├── 02-Variaveis.md
│ ├── 12-Ponteiros.md
│ ├── 15-Structs.md
│ ├── 05-OperacoesAritmeticas.md
│ └── 09-Controle-de-Fluxo.md
└── 5-Avancado
│ ├── 1-Ponteiro-Para-Funcao.md
│ ├── 2-Argumentos-de-linha-de-comando.md
│ ├── 1-1-qsort.md
│ ├── 3-Função-Recursiva.md
│ ├── 5-Threads.md
│ └── 4-ImplementacaoEdGenerica.md
├── README.MD
└── .github
├── id_4noobs_compact.svg
├── id_4noobs_line.svg
├── id_4noobs_expanded.svg
├── header-4noobs.svg
└── id_4noobs_slogan.svg
/src/imgs/c.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpaulohe4rt/c4noobs/HEAD/src/imgs/c.png
--------------------------------------------------------------------------------
/src/imgs/TiposPrimitivos.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpaulohe4rt/c4noobs/HEAD/src/imgs/TiposPrimitivos.png
--------------------------------------------------------------------------------
/src/4-Ordenacao/Exercicios.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpaulohe4rt/c4noobs/HEAD/src/4-Ordenacao/Exercicios.pdf
--------------------------------------------------------------------------------
/src/imgs/recursao_drawing_hands_escher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpaulohe4rt/c4noobs/HEAD/src/imgs/recursao_drawing_hands_escher.png
--------------------------------------------------------------------------------
/src/1-Introducao/2-Comunicacao.md:
--------------------------------------------------------------------------------
1 | ## 1.2 Comunicação
2 |
3 | Como iremos inicialmente usar o Github para hospedar o curso, gostariamos de pedir para qualquer dúvida referente à alguma aula ou melhorias que você [crie um ISSUE](https://github.com/jpaulohe4rt/c4noobs/issues/new) no nosso repositório, para que assim consigamos resolver e manter uma padronização de perguntas/respostas dentro do nosso projeto.
4 |
--------------------------------------------------------------------------------
/src/2-Ambiente/1-Editores-e-plugins.md:
--------------------------------------------------------------------------------
1 | ## 2.1 Editor de Texto
2 |
3 | Como a abordagem do curso será a parte básica do C, não será necessário o uso de uma IDE robusta, então utilizaremos o editor de texto VSCode para realizarmos os nossos códigos, existem outras opções como Dev C++, Code Blocks e etc. Então fiquem a vontade para utilizarem o editor que se sentirem mais confortáveis e realizar os códigos que serão apresentados ao longo do curso.
4 |
--------------------------------------------------------------------------------
/src/2-Ambiente/5-Ambiente-online.md:
--------------------------------------------------------------------------------
1 | # 2.5 Ambiente Online
2 |
3 | Outra opção rápida e simples para você utilizar com a linguagem C são os compiladores online.
4 |
5 | A minha recomendação é o site [glot.io](https://glot.io/), você pode escrever, compilar e salvar nele os seus códigos.
6 |
7 |
8 |
9 |
10 |
11 | Não é necessário criar uma conta para utilizar o site, somente se quiser salvar seus códigos nele.
--------------------------------------------------------------------------------
/src/2-Ambiente/3-Ambiente-linux.md:
--------------------------------------------------------------------------------
1 | # 2.3 Instalação Linux
2 |
3 | Para a instalação no Linux basta você baixar o arquivo no site [VSCode](https://code.visualstudio.com/) baixar e instalar o VSCode, para Linux.
4 |
5 |
6 |
7 |
8 |
9 | Depois de ter baixado o VSCode, você deve ir até Extensões (Ctrl+Shift+X) e instalar as seguintes extensões:
10 |
11 | 1.C/C++
12 |
13 |
14 |
15 |
16 | 2.C/C++ Compile Run
17 |
18 |
19 |
20 |
21 |
22 |
23 | 3.Instale o gcc.
24 |
25 |
26 | ```
27 | sudo apt-get install gcc
28 | ```
--------------------------------------------------------------------------------
/src/3-Basico/04-Constantes.md:
--------------------------------------------------------------------------------
1 | # 3.5 Constantes
2 |
3 | Em constantes, podemos utilizar números ou caracteres com valores predefinidos que não mudam e não podem ter seus valores alterados durante a execução do programa.
4 |
5 | Para criar uma constante é necessario a utilização do comando #define que é colocado no início do programa.
6 |
7 | ```c
8 | #include
9 |
10 | #define LARGURA 50 // Não se pode colocar ponto-e-vírgula depois do valor
11 | #define TAMANHO 1.70
12 |
13 | int main(void) {
14 |
15 | printf("%d\n", LARGURA);
16 | //Imprime:
17 | //50
18 |
19 | printf("%f\n", TAMANHO);
20 | //Imprime:
21 | //1.700000
22 |
23 | printf("%.2f\n", TAMANHO);
24 | //Imprime:
25 | //1.70
26 |
27 | //O ".2" define a precisão do número
28 | }
29 | ```
30 |
--------------------------------------------------------------------------------
/src/1-Introducao/1-Boas-vindas.md:
--------------------------------------------------------------------------------
1 | ## 1.1 Boas vindas
2 |
3 | Sejam bem vindos ao curso de C da He4rt Developers.
4 |
5 | Ficamos muito felizes de você ter chegado até aqui no nosso curso! Estaremos trabalhando nos próximos passos para que você saia desse curso entendendo o básico para começar a programar na linguagem C.
6 |
7 | C é uma linguagem de programação compilada de propósito geral, estruturada, imperativa, procedural, padronizada pela Organização Internacional para Padronização, criada em 1972 por Dennis Ritchie na empresa AT&T Bell Labs para o desenvolvimento do sistema operacional Unix.
8 |
9 | Ela é aplicada em sistemas operacionais (Linux), automóveis e aviões com microcontroladores, telefones e dispositivos portáteis com processadores embarcados, sistemas de aúdio e TV digital com processadores de sinais digital (DPS) entre diversas outras aplicações.
10 |
--------------------------------------------------------------------------------
/src/4-Ordenacao/Bubble Sort.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | void bubbleSort(int *vetor, int nelementos){
5 | int i, troca, aux;
6 | do {
7 | troca = 0;
8 | for(i = 0; i < nelementos - 1; i++){
9 | if(vetor[i] > vetor[i + 1]){
10 | printf("%d <-> %d\n", vetor[i], vetor[i + 1]); //saida
11 | aux = vetor[i];
12 | vetor[i] = vetor[i + 1];
13 | vetor[i + 1] = aux;
14 | troca = 69;
15 | }
16 | }
17 | nelementos--;
18 | } while(troca);
19 | }
20 |
21 | int main(){
22 | int i, nelementos;
23 | int *vetorptr = NULL;
24 |
25 | scanf("%d", &nelementos);
26 | if(!(vetorptr = (int *) calloc(nelementos, sizeof(int)))){
27 | printf("Erro de alocassaum\n");
28 | return 1;
29 | }
30 |
31 | for(i = 0; i < nelementos; i++)
32 | scanf("%d", &vetorptr[i]);
33 |
34 | bubbleSort(vetorptr, nelementos);
35 |
36 | for(i = 0; i < nelementos; i++){
37 | printf("%d", vetorptr[i]);
38 | i < nelementos - 1 ? printf(" ") : 0 ;
39 | }
40 |
41 | free(vetorptr);
42 | vetorptr = NULL;
43 | return 0;
44 | }
45 |
--------------------------------------------------------------------------------
/src/4-Ordenacao/Selection Sort.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | void selectionSort(int *vetor, int nelementos){
5 | int i, j, imenor, aux;
6 | for(i = 0; i < nelementos - 1; i++){
7 | imenor = i;
8 | for(j = i + 1; j < nelementos; j++)
9 | if(vetor[j] < vetor[imenor])
10 | imenor = j;
11 | printf("%d <-> %d\n", vetor[i], vetor[imenor]); //saida
12 | if(imenor != i){
13 | aux = vetor[i];
14 | vetor[i] = vetor[imenor];
15 | vetor[imenor] = aux;
16 | }
17 | }
18 | }
19 |
20 | int main(){
21 | int i, nelementos;
22 | int *vetorptr = NULL;
23 |
24 | scanf("%d", &nelementos);
25 | if(!(vetorptr = (int *) calloc(nelementos, sizeof(int)))){
26 | printf("Erro de alocassaum\n");
27 | return 1;
28 | }
29 |
30 | for(i = 0; i < nelementos; i++)
31 | scanf("%d", &vetorptr[i]);
32 |
33 | selectionSort(vetorptr, nelementos);
34 |
35 | for(i = 0; i < nelementos; i++){
36 | printf("%d", vetorptr[i]);
37 | i < nelementos - 1 ? printf(" ") : 0;
38 | }
39 |
40 | free(vetorptr);
41 | vetorptr = NULL;
42 | return 0;
43 | }
44 |
--------------------------------------------------------------------------------
/src/4-Ordenacao/Quick Sort.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | void quickSort(int *vetor, int inicio, int fim){
5 | int i = inicio, f = fim;
6 | int pivot = vetor[(inicio + fim) / 2];
7 | int aux;
8 | do {
9 | while(vetor[i] < pivot)
10 | i++;
11 | while(vetor[f] > pivot)
12 | f--;
13 | if(i <= f){
14 | aux = vetor[i];
15 | vetor[i++] = vetor[f];
16 | vetor[f--] = aux;
17 | }
18 | } while(i < f);
19 | if(inicio < f)
20 | quickSort(vetor, inicio, f);
21 | if(i < fim)
22 | quickSort(vetor, i, fim);
23 | }
24 |
25 | int main(){
26 | int i, elementos, pos1, pos2;
27 | int *vetorptr = NULL;
28 |
29 | scanf("%d", &elementos);
30 | if(!(vetorptr = (int *) calloc(elementos, sizeof(int)))){
31 | printf("Erro de alocassaum\n");
32 | return 1;
33 | }
34 |
35 | for(i = 0; i < elementos; i++)
36 | scanf("%d", &vetorptr[i]);
37 | scanf("%d %d", &pos1, &pos2);
38 |
39 | quickSort(vetorptr, 0, elementos - 1);
40 |
41 | printf("%d", vetorptr[--pos1] + vetorptr[--pos2]);
42 |
43 | free(vetorptr);
44 | vetorptr = NULL;
45 | return 0;
46 | }
47 |
--------------------------------------------------------------------------------
/src/2-Ambiente/4-Dicas-gerais.md:
--------------------------------------------------------------------------------
1 | # 2.4 Dicas Gerais
2 |
3 | ## Bibliografia
4 |
5 | Para auxiliar no seu aprendizado e fortalecer no conteúdo:
6 |
7 | 1. Cormen, T. et al., Algoritmos: Teoria e Prática. 3a ed., Elsevier - Campus, Rio de Janeiro, 2012
8 | 2. Ziviani, N., Projeto de Algoritmos com implementação em Pascal e C, 3a ed., Cengage Learning, 2010.
9 | 3. Felleisen, M. et al., How to design programs: an introduction to computing and programming, MIT Press, EUA, 2001.
10 | 4. Evans, D., Introduction to Computing: explorations in Language, Logic, and Machi nes, CreatSpace, 2011.
11 | 5. Harel, D., Algorithmics: the spirit of computing, Addison-Wesley, 1978.
12 | 6. Manber, U., Introduction to algorithms: a creative approach, Addison-Wesley, 1989.
13 | 7. Kernighan, Brian W; Ritchie, Dennis M.,. C, a linguagem de programacao: Padrao ansi. Rio de janeiro: Campus
14 | 9. Farrer, Harry. Programação estruturada de computadores: algoritmos estruturados. Rio de Janeiro: Guanabara Dois, 2002.
15 |
16 | ## Exercícios:
17 |
18 | O [URI Online Jugde](https://www.urionlinejudge.com.br/judge/pt/login) é uma ótima plataforma para você fazer exercicios relacionados a linguagem C e as demais.
--------------------------------------------------------------------------------
/src/3-Basico/11-Matrizes.md:
--------------------------------------------------------------------------------
1 | # Matrizes
2 |
3 | De acordo com a PUCRS:
4 |
5 | >Uma matriz é uma coleção de variáveis de mesmo tipo, acessíveis com um único nome e armazenados contiguamente na memória.
6 | >A individualização de cada variável de um vetor é feita através do uso de índices.
7 | >Os Vetores são matrizes de uma só dimensão.
8 | >(Prof. Márcio Sarroglia Pinho)
9 |
10 | Logo, uma matriz é um array de várias posições, portanto, também é um objeto.
11 |
12 | Declaração de Matrizes:
13 |
14 | ```c
15 | int vetor[5]; // declara um vetor de 5 posições
16 | int matriz[4][4]; // declara uma matriz de 4 linhas e 4 colunas
17 | ```
18 | Preencimento de vetor de 5 posições:
19 |
20 | ```c
21 | for ( i = 0; i < 5; i++ )
22 | {
23 | scanf ("%d", &vetor[ i ]);
24 | }
25 | ```
26 |
27 | Impressão de vetor de 5 posições:
28 |
29 | ```c
30 | for ( i = 0; i < 5; i++ )
31 | {
32 | printf ("%d", vetor[ i ]);
33 | }
34 | ```
35 |
36 | Preencimento de uma matriz 4x4:
37 |
38 | ```c
39 | for ( i = 0; i < 4; i++ )
40 | {
41 | for ( j = 0; j < 4; j++ )
42 | {
43 | scanf ("%d", &matriz[ i ][ j ]);
44 | }
45 | }
46 | ```
47 |
48 | Impressão de uma matriz 4x4:
49 |
50 | ```c
51 | for ( i = 0; i < 4; i++ )
52 | {
53 | for ( j = 0; j < 4; j++ )
54 | {
55 | printf ("%d", &matriz[ i ][ j ]);
56 | }
57 | }
58 | ```
--------------------------------------------------------------------------------
/src/3-Basico/08-Operadores-Logicos.md:
--------------------------------------------------------------------------------
1 | # Operadores Lógicos e de Comparação
2 |
3 | Os operadores lógicos são utilizados quando é necessário usar duas ou mais condições dentro da mesma instrução if para que seja tomada uma única decisão cujo resultado será verdadeiro ou falso.
4 |
5 | | Operadores Lógicos | Significado |
6 | |-----------------|----------------|
7 | | && | e |
8 | | ! | não |
9 | | \|\| | ou |
10 |
11 | Se você conhece algum tipo de logica de programação, já entende como eles funcionam, mas para esclarecer entrarei em mais detalhes.
12 |
13 | | Operadores de Comparação | Significado |
14 | | ----------------------- | ------------ |
15 | | > | maior que |
16 | | < | menor que |
17 | | >=| maior ou igual|
18 | | <= | menor ou igual |
19 | | == | igual |
20 | | != | não igual (diferente) |
21 |
22 | Esses operadores são como na matemática, é possível comparar valores na programação também, e os operadores lógicos entram nessa brincadeira também.
23 |
24 | Exemplos:
25 | ```c
26 | #include
27 |
28 | int main(void) {
29 |
30 | int idade;
31 |
32 | printf("Qual a sua idade? ");
33 | scanf("%d", &idade);
34 |
35 | if (idade >= 18) {
36 | printf("Você pode assistir esse filme\n");
37 | }
38 | else {
39 | printf("Você não tem idade suficiente para poder assistir esse filme\n");
40 | }
41 |
42 | }
43 | ```
--------------------------------------------------------------------------------
/src/2-Ambiente/2-Ambiente-windows.md:
--------------------------------------------------------------------------------
1 | # 2.2 Instalação Windows
2 |
3 | ## Utilizando Chocolatey:
4 |
5 | Instale o gerenciador de pacotes [Chocolatey](https://chocolatey.org/docs/installation) (leia os requisitos!!!!) e execute como administrador:
6 |
7 | (feche e reabra o shell após isso)
8 |
9 | `choco install vscode mingw`
10 |
11 | ## Instalando o MinGw:
12 |
13 | Instale o [mingw](https://sourceforge.net/projects/mingw/)
14 |
15 | Após fazer o download instale o mingw na sua máquina
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | ## Instalando Manualmente:
27 |
28 | Para a instalação no Windows basta você baixar o instalador no site [VSCode](https://code.visualstudio.com/) e instalar o VSCode.
29 |
30 |
31 |
32 |
33 |
34 | Depois de ter baixado o VSCode, você deve ir até Extensões (Ctrl+Shift+X) e instalar as seguintes extensões:
35 |
36 | 1.C/C++
37 |
38 |
39 |
40 |
41 |
42 | 2.C/C++ Compile Run
43 |
44 |
45 |
46 |
47 |
48 | 3.Instalar o [mingw](http://www.mingw.org/) e marquem as opções "mingw32-base" e "mingw32-gcc-g++".
49 |
--------------------------------------------------------------------------------
/src/3-Basico/01-PrimeirosPassos.md:
--------------------------------------------------------------------------------
1 | # 3.1 Primeiros Passos
2 |
3 | Programas em C consitem em uma ou mais partes que declaramos como funções e com a extensão do arquivo em **.c** como está representado na imagem a baixo.
4 |
5 |
6 |
7 |
8 |
9 | ## Comentários
10 |
11 | Comentários são partes do código que não são interpretadas pelo compilador. Normalmente utilizamos para explicar alguma parte do código que pode não estar muito clara.
12 |
13 | Para adicionar um comentário em seu programa inicie a linha com /* e termine o seu comentário */ ou // para comentário simples.
14 |
15 | ```
16 | // Este é um comentário simples
17 | ```
18 |
19 | ```
20 | /* Este é um exemplo
21 | com múltiplas linhas
22 | em C */
23 | ```
24 | Um programa C tem a seguinte estrutural geral:
25 |
26 | ```c
27 | // importação de arquivos/bibliotecas externas
28 | // e definição de elementos globais
29 |
30 | #include
31 |
32 | // funções, aqui que a programação começa
33 | int main()
34 | {
35 | declaração de variáveis
36 | ...
37 | sentenças
38 | ...
39 | }
40 | ```
41 | ### Mais um exemplo de um programa em C
42 |
43 | ```c
44 | #include
45 |
46 | int main(void)
47 | {
48 | printf("Hello World\n");
49 |
50 | return 0;
51 | }
52 | ```
53 |
54 | # Compilar e Executar
55 |
56 | A partir de agora, todo código que for exemplificado será compilado e executado segundo as seguintes linhas de comando:
57 | > gcc nomeDoArquivo.c -o codigoCompilado -Wall
58 |
59 | 1. gcc
60 | * Compilador
61 | 2. nomeDoArquivo.c
62 | * Código fonte
63 | 3. -o codigoCompilado
64 | * Parâmetro que compila o código para o nome desejado(codigoCompilado)
65 | 4. -Wall
66 | * Parâmetro para dar mais avisos(warning) que podem indicar erros no código
67 |
68 | > ./codigoCompilado
69 |
70 | * Executa o codigoCompilado
--------------------------------------------------------------------------------
/src/3-Basico/06-Funcoes.md:
--------------------------------------------------------------------------------
1 | # Funções
2 |
3 | As funções são maneiras de fazer um código separado da função principal, com o intuito de modularizar o código e isolar as responsabilidades.
4 |
5 |
6 | Para declarar uma função é necessário colocar o tipo de retorno dela, e o nome na frente, seguido dos parâmetros entre parênteses (variáveis enviadas na chamada da função) e fazer um escopo de bloco com chaves.
7 |
8 | As funções criadas devem vir antes da função main, caso contrário terão de ser criados protótipos.
9 |
10 |
11 | Um exemplo seria:
12 |
13 | ```c
14 | #include
15 |
16 |
17 | void mostrarVariavel(int x) { //função de retorno tipo void que recebe um valor inteiro e mostra ele na tela.
18 |
19 | //por ser void ela não precisa retornar nada.
20 |
21 | printf("%d\n", x);
22 | }
23 |
24 |
25 | int soma(int a, int b) { //função de retorno tipo inteiro que recebe duas variaveis inteiras na chamada e retorna a soma das duas.
26 |
27 | //como a função tem o tipo int, ela deve retornar um número inteiro.
28 |
29 | //o retorno é que quando você chamar a função irá aparecer ele no lugar, então vai aparecer a soma de dois valores passados.
30 | return a + b;
31 | }
32 |
33 | int main(void) {
34 | int a = 5;
35 | int b = 10;
36 |
37 | int c = soma(a, b); //chama a função soma, para somar os valores de a e b, atribuindo esse valor a soma.
38 |
39 | mostrarVariavel(c); //chama a função mostrar passando como parametro a variável c.
40 |
41 | }
42 | ```
43 |
44 | Se quisesse colocar a função abaixo da **main** teria que ser feito assim:
45 |
46 | ```c
47 | #include
48 | //prototipos
49 | int soma(int a, int b);
50 | void mostrarVariavel(int); //não é necessário colocar o nome da variável
51 | //fim prototipos.
52 |
53 | int main(void) {
54 | int a = 5;
55 | int b = 10;
56 |
57 | int c = soma(a, b); //chama a função soma, para somar os valores de a e b, atribuindo esse valor a soma.
58 |
59 | mostrarVariavel(c); //chama a função mostrar passando como parametro a variável c.
60 |
61 | }
62 |
63 |
64 | int soma(int a, int b) { //função de retorno tipo inteiro que recebe duas variaveis inteiras na chamada e retorna a soma das duas.
65 |
66 | //como a função tem o tipo int ela deve retornar um número inteiro.
67 |
68 | //o retorno é que quando você chamar a função irá aparecer ele no lugar, então vai aparecer a soma de dois valores passados.
69 | return a + b;
70 | }
71 |
72 | void mostrarVariavel(int x) { //função de retorno tipo void que recebe um valor inteiro e mostra ele na tela.
73 |
74 | //por ser void ela não precisa retornar algum valor.
75 |
76 | printf("%d\n", x);
77 | }
78 | ```
--------------------------------------------------------------------------------
/src/3-Basico/10-EstruturasDeRepeticao.md:
--------------------------------------------------------------------------------
1 | # Estruturas de Repetição
2 |
3 | Os comandos de repetição são um recurso que permite que um certo trecho do código de um programa seja repetido um certo número de vezes.
4 |
5 | Na liguagem C existem três comandos de repetição: while, do-while e for.
6 |
7 | ## While
8 |
9 | O **while** é uma estrutura que inicia **olhando a condição de parada**, se a condição de parada já tiver sido atingida não entra no bloco de repetição, se não tiver atingido a condição de parada roda o que tem dentro das chaves e depois verifica a condição novamente. A variável que vai gerar a condição de parada precisa ser criada fora da estrutura.
10 |
11 | ```c
12 | #include
13 |
14 | int main(void)
15 | {
16 | int contador = 1; //declarando e inicializando a variável de controle
17 |
18 | while (contador <= 10) // Testando a condição
19 | {
20 | printf("%d ", contador); //Executando um comando dentro do laço
21 |
22 | contador++; //atualizando a variável de controle
23 | }
24 | printf("\n");
25 |
26 | return 0;
27 | }
28 | ```
29 | ### Saída
30 |
31 |
32 |
33 |
34 | ## For
35 |
36 | O **for** é uma estrutura de repetição mais compacta, na qual, o bloco já determina **onde começa**, a **condição de parada** e o **passo dela** (que é como essa variável se comportará para atingir a condição de parada), não é necessário declarar a váriavel antes, ela pode ser criada ao criar o for.
37 |
38 | ```c
39 | //Exemplo1: Programa usando loop for em C
40 | #include
41 |
42 | int main(void)
43 | {
44 | int contador; //variável de controle do loop
45 |
46 | for(contador = 1; contador <= 20; contador++)
47 | {
48 | printf("%d ", contador);
49 | }
50 | printf("\n");
51 |
52 | return(0);
53 | }
54 | ```
55 | ### Saída
56 |
57 |
58 |
59 |
60 | ## Do/While
61 |
62 | O **do/while** é uma estrutura que inicia **sem olhar nenhuma condição de parada**, roda o que tem dentro das chaves e **depois verifica** a condição. A variável que vai gerar a condição de parada precisa ser criada fora da estrutura.
63 |
64 | ```c
65 | #include
66 |
67 | int main(void)
68 | {
69 | float nota1 = 0, nota2 = 0, media = 0;
70 | int resp;
71 |
72 | do
73 | {
74 | printf("Digite a primeira nota: ");
75 | scanf("%f",¬a1);
76 | printf("Digite a segunda nota: ");
77 | scanf("%f",¬a2);
78 |
79 | media = (nota1 + nota2)/2;
80 | printf("Media do aluno = %f\n",media);
81 |
82 | printf("Digite 1 para continuar ou 2 para sair\n");
83 | scanf("%d", &resp);
84 |
85 | }while (resp==1);
86 |
87 | return 0;
88 | }
89 | ```
90 | ### Saída
91 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/src/5-Avancado/1-Ponteiro-Para-Funcao.md:
--------------------------------------------------------------------------------
1 | # Ponteiro para função
2 |
3 | Um tópico que é pouco falado mas que pode ser muito importante para alguns projetos avançados é o ponteiro para funções. Esse tipo de ponteiro funciona parecido com os ponteiros comuns, com a diferença que ele aponta para uma função e não para uma variável.
4 |
5 | Declarar um ponteiro para função é simples:
6 |
7 | `**tipo de retorno** (* *nome da variável*)(**tipos dos parâmetros**)`
8 |
9 | ### Exemplo
10 |
11 | ```c
12 | // Função que será apontada
13 | int add(int a, int b) {
14 | return a + b;
15 | }
16 |
17 | // Outra função que será apontada
18 | int sub(int a, int b) {
19 | return a - b;
20 | }
21 |
22 | int main(void) {
23 | /* tipo de retorno
24 | | tipo do primeiro parâmetro
25 | | | tipo do segundo parâmetro
26 | | | | função que estamos apontando
27 | | | | |
28 | | | | |
29 | \ / \ / \ / \ /
30 | */ int (*func)(int, int) = add;
31 | /* / \
32 | |
33 | nome da variável
34 | */
35 | // O uso da função é idêntico ao uso normal
36 | printf("Função 1: %d\n", func(5, 5));
37 |
38 | // Pode substituir o valor a qualquer momento também
39 | func = sub;
40 | printf("Função 2: %d\n", func(5, 5));
41 | }
42 | ```
43 |
44 | ```
45 | Função 1: 10
46 | Função 2: 0
47 | ```
48 |
49 | Essa sintaxe para declarar é feia né? Da pra deixar mais bonito? Sim! Com um typedef é possível declarar esses ponteiros como se fossem variáveis normais.
50 | ```c
51 | typedef int (*Operacao)(int, int);
52 | /* / \
53 | |
54 | nome do tipo definido
55 | */
56 | int main(void) {
57 |
58 | Operacao op;
59 |
60 | op = add;
61 | printf("Função 1: %d\n", op(5, 5));
62 |
63 | op = sub;
64 | printf("Função 2: %d\n", op(5, 5));
65 | }
66 | ```
67 |
68 | Também é possível fazer vetores.
69 | ```c
70 | int main(void) {
71 |
72 | Operacao op[2];
73 |
74 | op[0] = add;
75 | printf("Função 1: %d\n", op[0](5, 5));
76 |
77 | op[1] = sub;
78 | printf("Função 2: %d\n", op[1](5, 5));
79 | }
80 | ```
81 |
82 | ### Qual a utilidade?
83 |
84 | [Leia esse tópico antes de continuar](./1-1-qsort.md)
85 |
86 | Qualquer algoritmo que seja desejavel rodar em qualquer estrutura de dados, como algoritmos de busca e ordenação, é um bom candidato para possuir uma função como parâmetro.
87 |
88 | Situações onde são esperadas funções com o mesmo objetivo mas com comportamento diferente também são bons candidatos (Atualização de entidades em um jogo, testes de desempenho customizado e etc)
89 |
90 |
91 | ### Referências:
92 | [qsort - documentação](https://www.cplusplus.com/reference/cstdlib/qsort/)
93 | [qsort - uso](https://www.galirows.com.br/meublog/programacao/utilizacao-funcao-qsort/)
94 | [tópico - stackoverflow](https://stackoverflow.com/questions/840501/how-do-function-pointers-in-c-work)
95 | [tópico - geeksforgeeks](https://www.geeksforgeeks.org/function-pointer-in-c/)
96 |
--------------------------------------------------------------------------------
/src/3-Basico/07-Arrays.md:
--------------------------------------------------------------------------------
1 | # Arrays
2 |
3 | Como C é uma linguagem bem próxima da memória, os arrays são alocações sequenciais na memória que armazenam um tipo de variável. Um array ou vetor, é uma estrutura que faz como se fosse um aglomerado de varias váriaveis de um mesmo tipo. Vale lembrar que um array é uma variável homogênea unidimensional, ou seja, um array só pode armazenar elementos do mesmo tipo básico(int, double, float, char, etc.) em uma única dimensão.
4 |
5 | Os arrays são de extrema importância para fazer armazenamentos rápidos e simples.
6 |
7 | Toda declaração de array **deve** ter a quantidade de posições, ou seja, quantas variáveis ou itens ele consegue armazenar.
8 |
9 | As posições se iniciam com 0 e vai até o tamanho declarado - 1.
10 |
11 | Exemplo de declaração de um array:
12 |
13 | ```c
14 | #include
15 |
16 | int main(void) {
17 | int array[10]; //declaração de um array de inteiros.
18 | //esse colchetes com 10 dentro significa que o array tem 10 posições.
19 |
20 | //o array tem posições de 0 a 9.
21 | }
22 | ```
23 |
24 |
25 |
26 |
27 | Para inserir dados no array segue o seguinte modelo:
28 |
29 | ```c
30 | #include
31 |
32 | int main(void) {
33 | int array[5] = {1, 2, 3, 4, 5};
34 |
35 | int array2[5];
36 |
37 | //abaixo inserindo em cada posição do array um valor.
38 | array2[0] = 1;
39 | array2[1] = 2;
40 | array2[2] = 3;
41 | array2[3] = 4;
42 | array2[4] = 5;
43 | }
44 | ```
45 |
46 | Existem outras formas de inserir dados no array,como:
47 | ```c
48 | #include
49 |
50 | int main(void) {
51 | int array[3] = {1, 2, 3};
52 | int array[] = {1, 2, 3};
53 | //Ambas as declarações são semelhantes, criamos um vetor com 3 posições(indíces de 0 a 2), e atribuímos 3 elementos à eles. Note que quando você atribui valores a um vetor na sua declaração, não é necessário você especificar o tamanho do vetor, já que essa informação é subentendida.
54 | }
55 | ```
56 | Também podemos inicializar um array com o valor 0, conforme os exemplos:
57 | ```c
58 | #include
59 |
60 | int main(void) {
61 | int array[5] = {1, 2, 3}; //Nesse caso, criamos um vetor de 5 posições e declaramos o valor das 3 primeiras posições. Nesse caso, o restante é preenchido com o valor 0.
62 | int array[100] = {0}; //Nesse caso, criamos um vetor de 100 posições e inicializamos TODAS as posições com o valor 0.
63 |
64 | }
65 |
66 | ```
67 |
68 | A estrutura de repetição for é bem utilizado com arrays, por exemplo, podemos somar todas as posições do array e fazer a média de itens lá dentro facilmente.
69 |
70 | Veremos ela com mais detalhes mais para frente, mas fique com uma prévia de como usa-la com arrays:
71 |
72 | ```c
73 | #include
74 |
75 | int main(void) {
76 | int array[5] = {1, 2, 3, 4, 5};
77 |
78 | int acumulaSoma = 0;
79 |
80 | for(int i = 0; i < 5; i++) {
81 | acumulaSoma += array[i];
82 | // a cada loop o i incrementa permitindo acessar todas as posições do array.
83 | }
84 |
85 | float media = acumulaSoma / 5;
86 | }
87 |
88 | Observação: Se você utilizar a palavra array como nome da variável vetor, é possível que sua IDE a marque como uma palavra reservada, no entanto, não se preocupe, pois seguindo os padrões da linguagem C, ela não é.
89 |
--------------------------------------------------------------------------------
/src/5-Avancado/2-Argumentos-de-linha-de-comando.md:
--------------------------------------------------------------------------------
1 | # Argumentos de Linha de Comando
2 |
3 | Provavelmente você já viu o cabeçalho `int main(int argc, char **argv)` em algum código na internet, esse cabeçalho é usado para que a função main seja capaz de receber argumentos por linha de comando e a partir desses argumentos definir comportamentos para o programa.
4 |
5 | Quando um programa é executado pelo terminal é possível passar parâmetros para ele. Veja o caso, por exemplo, do programa "ls": se executado com `ls`, `ls -a` ou `ls -lh`, apresentará um comportamento diferente em cada execução.
6 |
7 | `ls` :
8 | ```
9 | README.MD src
10 | ```
11 |
12 | `ls -lh`:
13 | ```
14 | total 4.0K
15 | -rwxrwxrwx 1 yansz yansz 3.1K Oct 14 13:54 README.MD
16 | drwxrwxrwx 1 yansz yansz 4.0K Oct 9 01:40 src
17 | ```
18 |
19 | `ls -a`:
20 | ```
21 | . .. .git .github .vscode README.MD src
22 | ```
23 |
24 | O programa ls é o mesmo, porém, os argumentos mudaram o comportamento, isso é apenas uma parte do poder dos argumentos linha de comando, é possível definir paths e outros comportamentos, o limite é sua criatividade!
25 |
26 |
27 |
28 | Caso esteja curioso, os programas como `ls`, `touch` e `cat` são apenas binários na pasta `/bin`, você pode inclusive botar seus próprios comandos nessa pasta.
29 |
30 | > :warning: Não abuse disso se você não souber usar
31 | ```
32 | user@c4noobs:/mnt/d/c4noobs$ sudo cp hello /bin
33 | user@c4noobs:/mnt/d/c4noobs$ hello
34 | Hello!
35 | ```
36 |
37 | ## Como usar?
38 |
39 | > test_args.c
40 | ```c
41 | #include
42 |
43 | int main(int argc, char **argv) {
44 | /* / \ / \
45 | | |
46 | | argumentos recebidos,
47 | | sendo o primeiro o nome do arquivo
48 | quantidade de argumentos recebidos
49 | */
50 | for(int i = 0; i < argc; i++) {
51 | printf("%d: %s\n", i, argv[i]);
52 | }
53 | printf("\n");
54 | }
55 | ```
56 | > :warning: Os argumentos são separados por espaços
57 | ```shell
58 | $ ./hello.exe c4noobs testes
59 | 0: ./hello.exe
60 | 1: c4noobs
61 | 2: testes
62 | ```
63 |
64 | ### Propriedades
65 |
66 | - É possível controlar o programa por fora do código.
67 | - Os argumentos são passados quando o programa é invocado.
68 | - O argv[1] armazena o primeiro argumento e assim por diante.
69 |
70 | ### Exemplos
71 |
72 | > vetor.c
73 | ```c
74 | #include
75 | #include
76 |
77 | int main(int argc, char **argv) {
78 | /*
79 | Esse programa espera um argumento externo,
80 | O número que representa o tamanho do vetor
81 | */
82 | if(argc != 2) {
83 | printf("Error: Invalid number of arguments\n");
84 | return -1;
85 | }
86 |
87 | // Atoi não é uma operação segura!
88 | int size = atoi(argv[1]);
89 |
90 | int *arr = (int *) malloc(size * sizeof(int));
91 | for(int i = 0; i < size; i++)
92 | arr[i] = i;
93 |
94 | // Print
95 | for(int i = 0; i < size; i++)
96 | printf("%d ", arr[i]);
97 | printf("\n");
98 | }
99 | ```
100 | ```shell
101 | user@c4noobs:/mnt/d/c4noobs$ ./vetor 5
102 | 0 1 2 3 4
103 | user@c4noobs:/mnt/d/c4noobs$ ./vetor
104 | Error: Invalid number of arguments
105 | ```
106 |
107 |
108 | ### Extras
109 |
110 | [Implementação do LS](https://www.google.com/search?q=creating+ls+c)
--------------------------------------------------------------------------------
/README.MD:
--------------------------------------------------------------------------------
1 |
93 |
--------------------------------------------------------------------------------
/src/5-Avancado/1-1-qsort.md:
--------------------------------------------------------------------------------
1 | ## Exemplos
2 |
3 | O maior exemplo de ponteiro para função está na função `qsort`, da stdlib, que tem o seguinte cabeçalho:
4 |
5 | ` void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));`
6 |
7 | A função qsort implementa o algoritmo [quicksort](https://pt.wikipedia.org/wiki/Quicksort) e busca conseguir se adequar a qualquer estrutura de dados, ela pode receber int, float, structs e etc.
8 |
9 | ### Usando a qsort
10 |
11 | Para utilizar a qsort é necessário definir uma função que consigar comparar dois valores, o mesmo comportamento que a `strcmp` da string.h apresenta, se o primeiro valor é maior que o segundo valor então a função **retorna um valor maior que 0**, se são iguais, então a função **retorna 0**, se é menor então **retorna um valor menor que 0**.
12 |
13 | | Return | p1, p2
14 | |----------|:-------------|
15 | | <0 | p1 < p2 |
16 | | 0 | p1 == p2 |
17 | | >0 | p1 > p2 |
18 |
19 | ```c
20 | int comparador(const void * a, const void * b) {
21 | int p1 = *((int*) a);
22 | int p2 = *((int*) b);
23 |
24 | if(p1 > p2)
25 | return 1;
26 | else if(p1 == p2)
27 | return 0;
28 | else
29 | return -1;
30 | }
31 | ```
32 |
33 | ```c
34 | int comparador(const void *a, const void *b) {
35 | return *(int*)a - *(int*)b;
36 | }
37 | ```
38 |
39 | ```c
40 | int main(void) {
41 | int size = 10;
42 | int * array = malloc(size * sizeof(int));
43 |
44 | // Valores aleatórios
45 | for(int i = 0; i < size; i++)
46 | array[i] = rand() % size;
47 |
48 | print(array, size);
49 | // Usando a qsort
50 | qsort(array, size, sizeof(int), comparador);
51 | print(array, size);
52 | }
53 | ```
54 |
55 | ```
56 | 3 6 7 5 3 5 6 2 9 1
57 | 1 2 3 3 5 5 6 6 7 9
58 | ```
59 |
60 | Esse comportamento funciona para qualquer estrutura de dados, por exemplo, se definirmos o tipo data basta definir a função de comparativo para que assim o algoritmo consiga ordenar um vetor de datas.
61 |
62 | ```c
63 | typedef struct data Data;
64 |
65 | struct data {
66 | int dia;
67 | int mes;
68 | int ano;
69 | };
70 |
71 | Data createData(int dia, int mes, int ano) {
72 | Data data;
73 | data.dia = dia;
74 | data.mes = mes;
75 | data.ano = ano;
76 |
77 | return data;
78 | }
79 |
80 | int compararData(const void * a, const void * b) {
81 | Data p1 = *((Data*) a);
82 | Data p2 = *((Data*) b);
83 |
84 | // Comparar Ano
85 | if(p1.ano > p2.ano) return 1; // Ano Maior
86 | else if(p1.ano < p2.ano) return -1; // Ano Menor
87 |
88 | // Ano Igual - Comparar Mês
89 | else if(p1.mes > p2.mes) return 1;
90 | else if(p1.mes < p2.mes) return -1;
91 |
92 | // Mês Igual - Comparar Dia
93 | else if(p1.dia > p2.dia) return 1;
94 | else if(p1.dia < p2.dia) return -1;
95 |
96 | // Tudo Igual
97 | return 0;
98 | }
99 |
100 | int main(void) {
101 |
102 | int size = 5;
103 | Data * array = malloc(size * sizeof(Data));
104 |
105 | array[0] = createData(10, 9, 2021);
106 | array[1] = createData(27, 5, 2001);
107 | array[2] = createData(25, 5, 2001);
108 | array[3] = createData(31, 12, 1917);
109 | array[4] = createData(11, 9, 2021);
110 |
111 | print(array, size);
112 | // Usando a srtcmp
113 | qsort(array, size, sizeof(Data), compararData);
114 | print(array, size);
115 | }
116 | ```
117 |
118 | ```
119 | Datas:
120 | 10/ 9/2021
121 | 27/ 5/2001
122 | 25/ 5/2001
123 | 31/12/1917
124 | 11/ 9/2021
125 |
126 | Datas:
127 | 31/12/1917
128 | 25/ 5/2001
129 | 27/ 5/2001
130 | 10/ 9/2021
131 | 11/ 9/2021
132 | ```
133 |
--------------------------------------------------------------------------------
/src/3-Basico/13-MemóriaDinâmica.md:
--------------------------------------------------------------------------------
1 | # Memória Dinâmica
2 |
3 | Até agora, durante o curso, trabalhamos apenas com variáveis alocadas na memória estática (AKA Stack), pelo compilador.
4 | O C, por padrão, aloca 8Mb da memória ram para a memória stack do programa. Entretanto, muitas vezes esses 8Mb podem ser
5 | ultrapassados, o que causa um erro bem famoso chamado Stack/Buffer Overflow, fazendo o seu programa finalizar a execução imediatamente, ainda podendo gerar uma grande falha de segurança.
6 | Se, por exemplo, você coleta uma string de um usuário através do scanf, ele poderia acabar com esse limite,
7 | ou então em uma leitura de arquivo que fosse maior que 8Mb.
8 |
9 | É aí que entra a memória dinâmica, conhecida como Heap: através das funções malloc e calloc, disponíveis na stdlib.h, você pode alocar o tamanho que você precisar da heap e
10 | acessa-la através de um ponteiro.
11 |
12 | E o uso dessa memória será muito importante para lidarmos com structs e estrutura de dados!
13 | Vamos ver como fica no código.
14 |
15 | # malloc
16 | ```c
17 | #include
18 | #include
19 |
20 | int main() {
21 | /* declaramos um ponteiro inteiro e atribuímos a ele
22 | * o endereço de memória retornado pela função malloc.
23 | * Na função malloc, colocamos como parâmetro quantos bytes queremos alocar na heap.
24 | * Usamos a função sizeof para passar o tamanho da variável, já que não podemos ter certeza do tamanho de cada variável,
25 | *já que isso pode mudar conforme o sistema.
26 | */
27 | int *p = (int *) malloc(sizeof(int));
28 |
29 | /* Diferente da memória stack, os bytes alocados na heap não são liberados ao finalizar o programa,
30 | * portanto devemos liberá-los de forma explícita
31 | * assim que o programa parar de usar a variável que está alocada na heap,
32 | * através da função free(), que recebe o ponteiro como parâmetro.
33 | */
34 | free(p);
35 | return 0;
36 | }
37 |
38 | ```
39 | OBS:
40 | O código abaixo compila, mas não é nada recomendável, pois
41 | a função malloc retorna um ponteiro void*, e queremos atribuí-lo a
42 | um ponteiro int*.
43 | ```c
44 | #include
45 | #include
46 |
47 | int main() {
48 | // sem conversão do ponteiro retornado :0
49 |
50 | int *p = malloc(sizeof(int));
51 |
52 | free(p);
53 |
54 | return 0;
55 | }
56 | ```
57 | # Falha na Alocação
58 | Podem ocorrer casos em que a alocação não é bem sucedida. Assim, a função malloc retorna NULL.
59 | Usar um poteiro NULL irá resultar em erro no programa e não queremos que isso aconteça.
60 | Assim, uma boa prática é criar uma condição para lidar com a alocação mal sucedida.
61 | ```c
62 | #include
63 | #include
64 |
65 | int main() {
66 | // sem conversão do ponteiro retornado :0
67 |
68 | int *p = malloc(sizeof(int));
69 |
70 | if (p == NULL) {
71 | printf("Alocação mal sucedida!");
72 | exit(1);
73 | }
74 | else free(p);
75 |
76 | return 0;
77 | }
78 | ```
79 | # calloc
80 | A função calloc faz o mesmo que o malloc, mas torna todos os bytes alocados iguais a zero.
81 |
82 | ```c
83 | #include
84 | #include
85 |
86 | int main() {
87 | /* Calloc recebe como parâmetro, além do tamanho, o número de alocações.
88 | * Se queremos alocar 3 inteiros, devemos chamar calloc(3, sizeof(int))
89 | */
90 | int *p = (int *) calloc(1, sizeof(int));
91 | if (p == NULL) {
92 | printf("Alocação mal sucedida!");
93 | exit(1);
94 | }
95 | printf("%d\n", *p);
96 |
97 | free(p);
98 | return 0;
99 | }
100 | ```
101 | Ao compilar e executar, o programa irá mostrar no stdout o número 0.
102 |
103 | # Criando arrays na heap
104 | Podemos criar um array na heap alocando múltiplos do tamanho da variável!
105 |
106 | ```c
107 | #include
108 | #include
109 |
110 | int main() {
111 | /* Cria um array de 5 inteiros na heap
112 | */
113 |
114 | int *nums = (int *) malloc(5*sizeof(int));
115 | if (nums == NULL) {
116 | printf("Alocação mal sucedida!");
117 | exit(1);
118 | }
119 | free(nums);
120 |
121 | return 0;
122 | }
123 | ```
124 |
125 |
--------------------------------------------------------------------------------
/src/4-Ordenacao/Merge Sort.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | typedef int data_t;
5 |
6 | void printArray(data_t vet[], size_t n)
7 | {
8 | for (size_t i = 0; i < n; i++)
9 | printf("%d ", vet[i]);
10 | printf("\n\n");
11 | }
12 |
13 | /*
14 | Função que recebe 2 vetores embutidos no vetor 'aux' através dos indices
15 |
16 | Exemplo: start = 0, mid = 2, end = 4
17 | isso quer dizer que de [0 .. 2] é um vetor e de [3 .. 4] o outro
18 | ambos vetores estão ordenado em relação a ele mesmo
19 |
20 | A função irá pegar em ordem do menor e colocar em aux, após irá devolver
21 | ordenado para o vet, com ordenação de [0 .. 4] no exemplo dado
22 |
23 | */
24 | void _merge(data_t vet[], data_t aux[], size_t start, size_t mid, size_t end)
25 | {
26 | size_t i = start;
27 | size_t j = mid + 1;
28 | size_t k = start;
29 |
30 | /* Consome do inicio de cada vetor, pegando sempre o menor primeiro */
31 | while (i <= mid && j <= end)
32 | if (vet[i] <= vet[j])
33 | aux[k++] = vet[i++];
34 | else
35 | aux[k++] = vet[j++];
36 |
37 | /* Caso reste algum elemento entre um dos dois vetores é consumido por fim */
38 | while (i <= mid)
39 | aux[k++] = vet[i++];
40 |
41 | while (j <= end)
42 | aux[k++] = vet[j++];
43 |
44 | /* Agora já ordenado os 2 vetores, devolve para o vet os elementos ordenados */
45 | for (i = start; i <= end; i++)
46 | vet[i] = aux[i];
47 | }
48 |
49 | /* Função local para gerar recursão da "divisão e conquista" do método */
50 | void _mergeSort(data_t vet[], data_t aux[], size_t start, size_t end)
51 | {
52 | if (start < end)
53 | {
54 | size_t mid = start + (end - start) / 2;
55 | _mergeSort(vet, aux, start, mid);
56 | _mergeSort(vet, aux, mid + 1, end);
57 | _merge(vet, aux, start, mid, end);
58 | }
59 | }
60 |
61 | /* Chamada mais amigável ao usuário */
62 | void mergeSort(data_t vet[], size_t vetLen)
63 | {
64 | /*
65 | MergeSort
66 |
67 | Custo assintótico de tempo para todos os casos: BigO(n*log(n))
68 | Espaço assintótico utilizado pelo método: BigO(n)
69 |
70 | obs: onde 'n' é o tamanho do vetor, no caso 'vetLen'
71 | */
72 |
73 | /*
74 | Alocação de um vetor de tamanho 'n' para uma única alocação de memoria para
75 | todo o método, trabalhando com subvetores contido dentro do vetor
76 | */
77 | data_t *aux;
78 | if (!(aux = (data_t *)malloc(vetLen * sizeof(data_t))))
79 | {
80 | printf("\n-[ERROR] Falha ao tentar alocar memoria para o vetor auxiliar\n\n");
81 | exit(3);
82 | }
83 |
84 | _mergeSort(vet, aux, 0, vetLen - 1);
85 |
86 | free(aux);
87 | }
88 |
89 | int main()
90 | {
91 | size_t i; /* Iterador do FOR */
92 | int vetLen; /* Quantidade de elementos no vetor (input user) */
93 | data_t *vet; /* Vetor de dados (input user) */
94 |
95 | /* Recebe do usuário o tamanho do vetor desejado */
96 | printf("Informe o comprimento do vetor: ");
97 | scanf("%d", &vetLen);
98 |
99 | /* Verifica quantidade nula ou negativa */
100 | if (vetLen < 1)
101 | {
102 | printf("\n-[ERROR] O comprimento do vetor deve ser um inteiro maior do que 0\n\n");
103 | return 1;
104 | }
105 |
106 | /* Aloca memoria para o vetor e verifica o sucesso do processo */
107 | if (!(vet = (data_t *)malloc(vetLen * sizeof(data_t))))
108 | {
109 | printf("\n-[ERROR] Falha ao tentar alocar memoria para o vetor\n\n");
110 | return 2;
111 | }
112 |
113 | /* Recebe do usuário os elementos do vetor */
114 | for (i = 0; i < vetLen; i++)
115 | {
116 | printf("Insira o vetor[%d]: ", i);
117 | scanf("%d", vet + i);
118 | }
119 |
120 | /* Printa vetor de entrada */
121 | printf("\nVetor de entrada:\n");
122 | printArray(vet, vetLen);
123 |
124 | /* Chama o método de ordenação MergeSort */
125 | mergeSort(vet, vetLen);
126 |
127 | /* Printa vetor ordenado */
128 | printf("Vetor ordenado:\n");
129 | printArray(vet, vetLen);
130 |
131 | /* Desaloca memoria alocada (*opcional, mas boa pratica) */
132 | free(vet);
133 |
134 | return 0;
135 | }
--------------------------------------------------------------------------------
/src/3-Basico/03-Entrada-Saida.md:
--------------------------------------------------------------------------------
1 | # 3.4 Entrada e Saída.
2 |
3 | Para que um programa em C mostre alguns resultados, ou que o programa peça algum tipo de dado ao usúario, precisamos utilizar funções existentes em C que são chamadas de `printf()` e `scanf()`. Lembrando que, é necessário utilizar no início do programa : `#include ` para que essas funções possam ser utilizadas.
4 |
5 |
6 | ## printf()
7 |
8 | A função `printf()` exibe um ou mais dados na tela. Contudo ele deve receber pelo menos dois parâmetros, separados por vírgula.
9 |
10 | - Uma string que define, através de caracteres especiais, os dados a serem impressos e a suas posições na linha da impressão ou um dado a ser impresso.
11 |
12 |
13 | ### Exemplos:
14 |
15 | Para imprimir string:
16 |
17 | ```c
18 | printf("%s", "linguagemC");
19 | ```
20 |
21 | - "%s": é a string para resgatar o valor que vai ser impresso
22 | - "linguagemC": é o valor que vai ser impresso
23 |
24 | É possível incluir um texto dentro de uma variável string de formato %s e será compilado de acordo com o que foi digitado no programa.
25 |
26 | ```c
27 | printf("O jogador %s machucou o tornozelo", "Neymar");
28 | ```
29 |
30 | Depois de ser compilado o resultado é:
31 |
32 | ```c
33 | O jogador Neymar machucou o tornozelo
34 | ```
35 |
36 | Você também pode inserir códigos especiais e alguns tipos de formatação no **printf()**.
37 |
38 | ## Tabela de Códigos Especiais:
39 |
40 | |Código Especiais | Significado |
41 | |-----------------| ------------
42 | | \n| Nova linha |
43 | | \t | Tabulação |
44 | | \a | Beep - Toque do auto falante |
45 | | \\\ | Barra invertida |
46 | | \\" | Aspas duplas|
47 |
48 | ## Tabela de Formatação
49 |
50 | |Código Especiais | Significado |
51 | |-----------------| ------------|
52 | | %c | Imprime um caractere simples |
53 | | %d | Imprime um número inteiro com sinal |
54 | | %f | Imprime um ponto flutuante (número real) |
55 | | %.3f | Imprime um ponto flutuante com 3 casas decimais |
56 | | %o | Imprime um decimal como octal |
57 | | %x | Imprime um decimal como hexadecimal |
58 | | %s | Imprime uma string de caracteres |
59 | | %% | Imprime um caractere % |
60 | | %ld | Imprime um número inteiro longo com sinal |
61 | | %p | ponteiro (endereço da memória) |
62 | [Mais detalhes sobre formatação](http://www.cplusplus.com/reference/cstdio/printf/)
63 |
64 | Exemplo:
65 |
66 | ```c
67 | printf("Total de Funcionários: %d \n Lista de Funcionários: \n\t %s \n\t %s \n", 2, "Fulano de Tal", "Ciclano Beltrano");
68 |
69 | //E será impresso
70 |
71 | Total de Funcionários: 2
72 | Lista de Funcionários:
73 | Fulano de Tal
74 | Ciclano d Beltrano
75 |
76 | // No printf podemos fazer algo que não é possível no scanf, utilizar %s em uma string com espaço, como no nome Ciclano Beltrano
77 | // Podemos ver acima que para imprimir funciona, porém abaixo ensinarei um truque para leitura
78 | ```
79 |
80 |
81 | ## scanf()
82 |
83 | A função é similar ao printf, os caracteres obtidos pelo **scanf()** são realizados pelo teclado. Este comando efetua uma leitura do teclado onde é esperada uma variável e o valor lido será armazenado no endereço da variável do programa. Para ficar mais claro segue o exemplo:
84 |
85 | ```c
86 | #include
87 |
88 | int main(void){
89 | int a, b, soma; // declaração das variáveis de tipo inteiro
90 |
91 | printf("Digite um numero inteiro: ");
92 | scanf("%d", &a); // recebe um inteiro e armazena na variável a
93 |
94 | printf("Digite um numero inteiro: ");
95 | scanf("%d", &b); // recebe um inteiro e armazena na variável b
96 |
97 | soma = a + b; // Efetua adição de a com b e armazena na variável soma
98 |
99 | //scanf("%d %d", &a, &b); também pode ser feito, e mesmo que o usário insira o primeiro numero, aperte enter ou espaço
100 | //e insira o segundo numero, funcionará. Só não funcionará se colocar os dois numeros juntos, pois será lido como um só
101 |
102 | printf("O valor da soma = %d\n", soma); // Mostra a mensagem com o resultado
103 |
104 | return 0;
105 | }
106 | ```
107 |
108 | - Para a leitura de strings (conjunto de letras) com espaço entre elas, o segredo é usar um comando que diz ao computador para ler até o usuario apertar enter
109 |
110 | ```c
111 | #include
112 |
113 | int main(void){
114 | char vetor[10]; // Explicamos melhor um vetor mais a frente
115 | scanf("%[^\n]", vetor); //e também porque nao foi usado &
116 | // Dessa forma poderemos ler strings como "Ciclano Beltrano" sem qualquer problema
117 | // Se tivessemos utilizado %s o computador teria lido somente o que estava a esquerda do espaço, o Ciclano
118 | }
119 |
120 |
--------------------------------------------------------------------------------
/src/3-Basico/14-Arquivos.md:
--------------------------------------------------------------------------------
1 | # Leitura e Escrita de Arquivos
2 |
3 | Além da possibilidade de exibir resultados do usuário, também é possivel fazer a leitura e escrita de arquivos em C. As funções necessárias para tratar arquivos estão incluídas na biblioteca `#include `.
4 |
5 | ## FILE
6 |
7 | Assim como os tipos `int`, `float`, `double` e `char`, o tipo FILE é responsável por armazenar informações para controlar o arquivo que será aberto pelo `fopen()`
8 |
9 | ## fopen()
10 |
11 | A função `fopen()` é responsável por abrir um arquivo. Para utilizar ela, é necessário incluir dois argumentos, sendo o primeiro o **caminho do arquivo** e o segundo é o **modo** que esse arquivo será tratado. Os modos são:
12 |
13 | | Modo | Função |
14 | | ---- | ------ |
15 | | r | Abre o arquivo para leitura |
16 | | r+ | Abre o arquivo para leitura e escrita |
17 | | w | Abre o arquivo para escrita. Se já existir, o conteúdo anterior será destruído. Caso contrário, um novo arquivo é criado. |
18 | | w+ | Abre o arquivo para leitura e escrita. Se já existir, o conteúdo anterior será destruído. Caso contrário, um novo arquivo é criado. |
19 | | a | Abre o arquivo para escrita. Os dados são incluídos a partir do final. O arquivo é criado caso não exista |
20 | | a+ | Abre o arquivo para leitura e escrita. A escrita é sempre inserida no final do arquivo, mas a posição inicial de leitura depende do sistema. O arquivo é criado caso não exista. |
21 | | b | Inserir `b` no final dos modos anteriores permite a leitura ou escrita de arquivos binários |
22 |
23 | > Se ocorrer algum erro ao abrir um arquivo, a função retorna **NULL**.
24 |
25 | > A função retorna um ponteiro do tipo **FILE**(FILE*).
26 |
27 | ## fclose()
28 |
29 | A função `fclose()` encerra o uso do arquivo aberto anteriormente pelo `fopen()` e **salva** as alterações feitas.
30 |
31 | > Se o arquivo foi encerrado com sucesso, é a função retorna **0**. Caso contrário um erro ocorre.
32 |
33 | ## fprintf()
34 |
35 | A função `fprintf()`, muito semelhante a `printf()`, permite a escrita de texto nos arquivos abertos anteriormente pelo `fopen()`. O primeiro argumento é do tipo **FILE***, o segundo é o formato da string que será escrita e o terceiro(opcional) são os valores que serão incluídos na string formatada anterior.
36 |
37 | ## fscanf()
38 |
39 | A função `fscanf()`, muito semelhante a `scanf()`, permite leitura de texto nos arquivos abertos anteriormente pelo `fopen()`.O primeiro argumento é do tipo **FILE***, o segundo é o formato da string que será lida e o terceiro são as variáveis que vão armazenar os dados.
40 |
41 | ## Exemplo de uso
42 |
43 | ## Escrita de Inteiros
44 |
45 | > Código-fonte
46 |
47 | ```c
48 | #include
49 | #include
50 |
51 | int main() {
52 | // Variável para armazenar quantos inteiros vão ser inseridos
53 | int n = 10;
54 | int i;
55 |
56 | // Cria a variável do tipo FILE* e abre o arquivo para escrita em seguida
57 | FILE *arquivo;
58 | arquivo = fopen("valores.txt", "w");
59 | if (arquivo == NULL) {
60 | printf("ERRO AO ABRIR O ARQUIVO\n");
61 | exit(-1);
62 | }
63 |
64 | // Loop que salva valores entre 0 e n separados por espaço
65 | for (i = 0; i < n; i++) {
66 | fprintf(arquivo, "%d ", i);
67 | }
68 |
69 | int sucesso = fclose(arquivo);
70 | if (sucesso != 0) {
71 | printf("ERRO AO ENCERRAR O ARQUIVO\n");
72 | exit(-1);
73 | }
74 | return 0;
75 | }
76 | ```
77 |
78 | ## Leitura de Inteiros
79 |
80 | Considere que, na mesma pasta no qual o código-fonte tenha um arquivo com nome `valores.txt` e **10** valores **separados por espaço**:
81 | > valores.txt
82 | ```
83 | 0 1 2 3 4 5 6 7 8 9
84 | ```
85 |
86 | > Código-fonte
87 |
88 | ```c
89 | #include
90 | #include
91 |
92 | int main() {
93 | // Número de valores armazenados no valores.txt
94 | int n = 10;
95 | int i;
96 | // Inicia um array alocado dinâmicamente com tamanho n
97 | int *valores = (int*) malloc(sizeof(int)*n);
98 |
99 | // Cria a variável do tipo FILE* e abre o arquivo para leitura em seguida
100 | FILE *arquivo;
101 | arquivo = fopen("valores.txt", "r");
102 |
103 | if (arquivo == NULL) {
104 | printf("ERRO AO ABRIR O ARQUIVO\n");
105 | exit(-1);
106 | }
107 |
108 | // Faz a leitura do arquivo para encontrar os valores indicados pelo formato da string
109 | for (i = 0; i < n; i++) {
110 | fscanf(arquivo, "%d ", &valores[i]);
111 | }
112 |
113 | // Exibe os valores obtidos e armazenados anteriormente
114 | for (i = 0; i < n; i++) {
115 | printf("%d ", valores[i]);
116 | }
117 | printf("\n");
118 |
119 | int sucesso = fclose(arquivo);
120 | if (sucesso != 0) {
121 | printf("ERRO AO ENCERRAR O ARQUIVO\n");
122 | exit(-1);
123 | }
124 |
125 | // Libera os valores da memória
126 | free(valores);
127 | return 0;
128 | }
129 | ```
--------------------------------------------------------------------------------
/src/3-Basico/02-Variaveis.md:
--------------------------------------------------------------------------------
1 | # 3.3 Variáveis
2 |
3 | Variável é uma referência de um valor que é armazenado na memória do sistema.
4 | Ela pode ter o seu conteúdo alterado por um comando de atribuição.
5 | Depois da atribuição feito pelo comando elas mudam de valor.
6 |
7 | ```c
8 | int a, b, c;
9 | a = 3; // a recebe o valor 3
10 | b = a * 2; // b recebe o dobro do valor de a
11 | c = a + b + 2; // c recebe 11
12 | ```
13 |
14 |
15 | ## Tipo de Variáveis
16 |
17 | Na Linguagem C todas as variáveis tem um tipo, esses tipos definem os valores que a variável pode armazenar e o quanto ocupa na memória do sistema.
18 |
19 | | Tipo | Valores Aceitos | Tamanho na Memória |
20 | | --- | --- | --- |
21 | | char | letras e símbolos: 'a', 'B', '#', '^', '1', '9'| 1 byte |
22 | | int | de -32767 até 32767 (apenas números inteiros) | 4 bytes |
23 | | float | de -3.4 x 1038 até +3.4 x 10+38com até 6 dígitos de precisão | 4 bytes |
24 | | double | de -1.7 x 10308 até +1.7 x 10+308com até 10 dígitos de precisão| 8 bytes |
25 |
26 |
27 | ## Declaração de Variáveis
28 |
29 | - Devem ser declaradas antes de serem usadas.
30 | - Não há uma inicialização implícita na declaração.
31 |
32 |
33 | ## Requisitos de Variáveis
34 |
35 | Precisamos seguir alguns requisitos essenciais para manter o nosso código limpo e claro:
36 |
37 | - Deve iniciar com uma letra ou com um "_".
38 | - Tem que ter no máximo de 31 caracteres.
39 | - Pode conter números a partir do segundo caracter.
40 | - Recomendo separar minúsculas e maiúsculas para separar palavras: "he4rtDevelopers".
41 | - Tem que usar nomes que sejam significativos e que façam sentido dentro do seu programa.
42 | - Deve ser diferente dos comandos da linguagem.
43 |
44 | Alguns exemplos válidos:
45 |
46 | ```
47 | Cidade, Contador, LadoA, userName, User_1
48 | ```
49 |
50 | Alguns exemplos inválidos:
51 |
52 | ```
53 | 123var, nome completo, #idade, double, break
54 | ```
55 |
56 | Como ficam no código:
57 |
58 | ```c
59 | #include // Inicialização do arquivo de cabeçalho
60 |
61 | int main(void)
62 | {
63 | int contador; // declarações simples
64 | float PrecoDoQuilo;
65 | double TaxaDeCambio;
66 | char letra;
67 | char nome[10]; // Para declarar um vetor é necessário [] e dizer quantos caracteres ou inteiros caberá nele
68 |
69 | int TamanhoManoel, TamanhoJoao, TamanhoMaria; // Pode colocar mais de uma variável na mesma linha
70 |
71 | double TaxaDeImportacao,
72 | TaxaDeEmbarque,
73 | TaxaDoBagagem, // Também pode trocar de linha no meio
74 | TaxaDeCheckin;
75 | }
76 | ```
77 |
78 |
79 | ## Escopo de Variáveis
80 |
81 | O local onde declaramos a variável define até onde ocorrerá seu funcionamento dentro do código.
82 | Seus subtipos são:
83 |
84 | - Local - funciona somente no local que foi declarada, ou seja, dentro da função onde ela 'nasceu' e as funções dentro desta e se encerra quando a função acaba sua execução. Então toda vez que a função é chamada, a variável local surge e termina junto com a execução da função.
85 |
86 | - Global - funciona em qualquer parte do código, é declarada em somente um local e pode ser utilizada em qualquer local das funções. Então sempre tenha muito cuidado ao utiliza-la, pois a alteração de seu conteudo pode ser feita em qualquer parte.
87 |
88 | ```c
89 | #include
90 |
91 | int global; // É declarada antes de todas as outras funçoes e depois das bibliotecas
92 |
93 | int main(void)
94 | {
95 |
96 | int local; // Declarada dentro da função, nesse caso o main(void) e poderá ser utilizada por tudo que está dentro dele
97 |
98 | int funcao( int local )
99 | {
100 | printf("%d \n", local); // Se temos a variável local do main e a local da função, a prioridade é da variável da função
101 | //e por mais que tenham o mesmo nome, alterar a local da função não altera a local do main, somente caso a da função não existisse
102 |
103 | printf("%d \n", global); // A global funcionará pelo que foi dito anteriormente
104 | }
105 |
106 | //Por favor, não declarem 2 variáveis com o mesmo nome, fiquem atentos
107 |
108 | }
109 | ```
110 |
111 |
112 | ### Como inicializar uma variável de declaração
113 |
114 | ```c
115 | #include // Inicialização do arquivo de cabeçalho
116 |
117 | int main(void)
118 | {
119 | int Peso = 0; // declara a variável (int) e inicializa com Zero
120 | float PrecoDoPao = 10.53; // declara a variável (float) e inicializa com 10.53
121 | double TaxaDoImposto= 1.8,
122 | TaxaDoCobrador = 1.956,
123 | TaxaDeDeslocament0 = 1.75;
124 | int valores[] = {1, 2, 3, 4, 5, 6}; // declara o vetor de inteiros e atribui a seu tamanho a quantidade de elementos
125 | // que neste caso temos 6 elementos inicializados
126 |
127 | char vetor[] = {"c4noobs"}; // declarando vetor de letras e inicializa com uma string
128 | }
129 | ```
130 | O ruim desta forma de declaração é que não podemos fazer inclusões no vetor, e aumentar seu tamanho para receber mais conteúdo demanda um conhecimento avançado que traremos futuramente
131 |
--------------------------------------------------------------------------------
/src/3-Basico/12-Ponteiros.md:
--------------------------------------------------------------------------------
1 | # Ponteiros
2 | Para muitas pessoas ponteiros são de arrepiar os cabelos... Eles são um pouco complexos mas o básico deles é simples de entender.
3 |
4 | Um ponteiro é quando você reserva uma posição de memória que armazena uma quantidade de bytes relativo a cada tipo.
5 |
6 | >Imagem retirada do repositório do hellowluan
7 |
8 |
9 |
10 | Cada tipo de variável tem um gasto na memória RAM do seu computador, um ponteiro armazena um tamanho necessário para receber esse tipo. O ponteiro contém o endereço de memória que foi reservado para fazer sua alocação, porém, para armazenar o ponteiro também teremos um gasto que será independente ao tipo de dado que iremos apontar, e será definido pela arquitetura do seu processador, se for x64, o tamanho do ponteiro será de 64 bits **(8 bytes)**, ou 32 bits **(4 bytes)** caso contrário. Podemos verificar essa afirmação com o operador **sizeof**
11 |
12 | Assim como as funções do ****, para manipular ponteiros e realizar alocação dinâmica é necessário utilizar o header ****.
13 |
14 | Exemplo inicial:
15 |
16 | ```c
17 | #include
18 | #include
19 |
20 | int main(void) {
21 |
22 | int valor = 30;
23 |
24 | int *ponteiro = NULL; /* Para declarar um ponteiro, definimos seu tipo
25 | e o nome com um asterisco como prefixo.
26 | Chamados de 'ponteiro para inteiro'
27 | pois o ponteiro aponta para um valor inteiro */
28 |
29 | // NULL é um ponteiro nulo, que não aponta para um
30 | // endereço válido, e é usado para indicar que o ponteiro
31 | // está vazio. Isso é útil para verificarmos a disponibilidade
32 | // do mesmo, pois não queremos cair no erro de "segmentation fault"
33 | // (erro de memória inexistente)
34 |
35 | ponteiro = &valor; /* como o ponteiro armazena um endereço
36 | de memória, utilizamos o operador '&'
37 | para informar que estamos passando um
38 | endereço e não um valor. */
39 |
40 |
41 | // Se tentarmos imprimir o valor do ponteiro
42 | // iremos nos deparar com o endereço que é
43 | // armazenado por ele
44 | printf("Endereço do ponteiro na memória: %p\n", ponteiro);
45 |
46 | // Então se quisermos o valor para qual o endereço aponta
47 | // precisamos desreferenciar o seu valor, indicamos isso
48 | // usando o operador * (operador de desreferenciação)
49 | printf("Variável valor: %d, Ponteiro: %d\n", valor, *ponteiro);
50 | // imprime
51 | // Variável valor: 30, Ponteiro: 30
52 |
53 | // Se quisermos atribuir um valor diretamente à variável "ponteiro"
54 | // precisaremos informar com o operador de desreferenciação '*'
55 | // que é a forma que temos de acessar o valor armazenado
56 | // pelo ponteiro
57 | (*ponteiro) = 60; /* Informamos que estamos trabalhando com valores
58 | não com endereços */
59 |
60 | // Agora, se colocar-mos para imprimir os valores das variáveis
61 | // "ponteiro" e "valor" iremos ter uma surpresinha
62 | printf("Variável valor: %d, Ponteiro: %d\n", valor, *ponteiro);
63 | // imprime
64 | // Variável valor: 60, Ponteiro: 60
65 |
66 | // Note que em momento algum alteramos a variável "valor" diretamente.
67 | // Essa é uma característica dos ponteiros, quando recebemos
68 | // o endereço da variável, agora temos formas distintas de manipular
69 | // a mesma variável, pois estamos compartilhando o mesmo endereço.
70 |
71 | return 0;
72 | }
73 | ```
74 |
75 | É possível também reservar uma parte da memória sem passar uma variável para o ponteiro. É utilizada a função malloc com a sizeof.
76 |
77 | ```c
78 | #include
79 | #include
80 |
81 | int main(void) {
82 |
83 | int *ponteiro; // para declarar um ponteiro, definimos o seu tipo
84 | // e o nome com o asterisco atrás dele.
85 |
86 | ponteiro = (int *)malloc(sizeof(int));
87 | //a função malloc armazena na memória o tamanho em bytes que você quiser.
88 | //a função sizeof retorna o tamanho do tipo que você quer, portanto o ponteiro fica alocado corretamente para o tamanho indicado e está pronto para uso.
89 |
90 | //como um ponteiro já é um endereço de memória
91 | //não é necessário utilizar o "&", como é feito normalmente
92 | scanf("%d", ponteiro);
93 | printf("%d\n", *ponteiro);
94 |
95 | }
96 | ```
97 |
98 | Má prática com ponteiros:
99 | ```c
100 | #include
101 | #include
102 |
103 | int main(void) {
104 |
105 | int *ponteiro;
106 |
107 | ponteiro = (int *)malloc(sizeof(int));
108 |
109 | //esse código funciona mas não é adequado
110 | //ao compilar o compilador dá diversos warning(avisos)
111 | //pois os tipos das variáveis não é o mesmo da formatação
112 | //podendo resultar em ações indesejadas
113 | scanf("%d", &ponteiro);
114 | printf("%d\n", ponteiro);
115 |
116 | }
117 | ```
118 |
119 | Maiores explicações sobre C:
120 |
121 | Embarcados
122 |
123 | USP
124 |
125 |
126 |
--------------------------------------------------------------------------------
/src/3-Basico/15-Structs.md:
--------------------------------------------------------------------------------
1 | # Structs
2 | **Structs**, também conhecidas como **Registros**, definem um tipo de dado que agrupa outras variáveis dentro dele mesmo, como se fossem atributos daquela variável.
3 |
4 | Por exemplo, eu posso criar um tipo de dado chamado **Pessoa** e posso definir que **Pessoa** têm ***idade***, ***altura*** e ***peso***
5 |
6 | ```c
7 | #include
8 | #include
9 |
10 | typedef struct // Cria uma STRUCT para armazenar os dados de uma pessoa
11 | {
12 | int idade; // Define o campo idade
13 | float altura; // Define o campo altura
14 | float peso; // Define o campo peso
15 | } Pessoa; // Define o nome do novo tipo criado. No caso, Pessoa
16 | ```
17 | Dessa forma, nós acabamos de criar nosso próprio tipo de dado, e podemos definir variáveis do tipo **Pessoa**:
18 | ```c
19 | int main() {
20 |
21 | Pessoa julio, heloisa; // Cria variáveis do tipo Pessoa (julio e heloisa)
22 |
23 | return 0;
24 | }
25 | ```
26 | Cada variável do tipo **Pessoa** têm campos dentro dela como a ***idade***, ***altura*** e ***peso***. Mas você me pergunta, como eu posso acessar esses campos? Muito simples! Basta utilizar a sintaxe **nomeDaVariavel.nomeDoCampo**:
27 | ```c
28 | int main() {
29 |
30 | Pessoa julio, heloisa;
31 |
32 | // Define os campos do julio
33 | julio.idade = 21;
34 | julio.altura = 1.70;
35 | julio.peso = 50.6;
36 |
37 | // Define os campos da heloisa
38 | heloisa.idade = 23;
39 | heloisa.altura = 1.60;
40 | heloisa.peso = 47;
41 |
42 | // Printa os dados do Julio
43 | printf("Dados do Julio:\n");
44 | printf("Idade: %d\n", julio.idade);
45 | printf("Altura: %.2f\n", julio.altura);
46 | printf("Peso: %.2f\n", julio.peso);
47 |
48 | // Printa os dados da Heloisa
49 | printf("\nDados da Heloisa:\n");
50 | printf("Idade: %d\n", heloisa.idade);
51 | printf("Altura: %.2f\n", heloisa.altura);
52 | printf("Peso: %.2f\n", heloisa.peso);
53 |
54 | return 0;
55 | }
56 | ```
57 | Também é possível definir um Array de **Pessoa**, como o exemplo a seguir:
58 | ```c
59 | int main () {
60 |
61 | Pessoa listaDePessoas[5]; // Cria um Array do tipo pessoa
62 |
63 | // Conseguimos definir os campos da Pessoa utilizando o índice do Array
64 | listaDePessoas[0].idade = 32; // Define a idade da pessoa do índice [0]
65 | listaDePessoas[0].altura = 1.78; // Define a altura da pessoa do índice [0]
66 | listaDePessoas[0].peso = 67; // Define o peso da pessoa do índice [0]
67 |
68 | listaDePessoas[1].idade = 18; // Define a idade da pessoa do índice [1]
69 | listaDePessoas[1].altura = 1.67; // Define a altura da pessoa do índice [1]
70 | listaDePessoas[1].peso = 60; // Define o peso da pessoa do índice [1]
71 |
72 | // Como no exemplo anterior, aqui eu também consigo Printar os dados de cada pessoa no Array
73 | printf("Dados da pessoa do indice [0]:\n");
74 | printf("Idade: %d\n", listaDePessoas[0].idade);
75 | printf("Altura: %.2f\n", listaDePessoas[0].altura);
76 | printf("Peso: %.2f\n", listaDePessoas[0].peso);
77 |
78 | printf("\nDados da pessoa do indice [1]:\n");
79 | printf("Idade: %d\n", listaDePessoas[1].idade);
80 | printf("Altura: %.2f\n", listaDePessoas[1].altura);
81 | printf("Peso: %.2f\n", listaDePessoas[1].peso);
82 |
83 | return 0;
84 | }
85 | ```
86 | Também posso preencher o Array de outras formas mais eficientes, criando uma variável do tipo ***Pessoa***, atribuindo os dados a ela e definindo-a como parte da ***listaDePessoas***, dessa forma poderemos fazer um mini-programa de cadastro de Pessoas:
87 | ```c
88 | // Esse é um simples programa de cadastro, nesse caso de no máximo 5 pessoas
89 |
90 | #include
91 | #include
92 |
93 | typedef struct
94 | {
95 | int idade;
96 | float altura;
97 | float peso;
98 | } Pessoa;
99 |
100 |
101 | int main () {
102 |
103 | Pessoa listaDePessoas[5]; // Definimos um Array de 5 posições
104 | int indice = 0; // Define um indice inicial para controlar a estrutura de repetição;
105 |
106 | Pessoa p; // Variável Pessoa apenas para armazenar os dados da Pessoa durante o cadastro
107 |
108 | printf("Bem-vindo ao cadastro de pessoas!\n\n");
109 | do
110 | {
111 | printf("Digite a idade da pessoa %d: ", indice+1);
112 | scanf("%d", &p.idade);
113 |
114 | printf("Digite a altura da pessoa %d: ", indice+1);
115 | scanf("%f", &p.altura);
116 |
117 | printf("Digite o peso da pessoa %d: ", indice+1);
118 | scanf("%f", &p.peso);
119 |
120 | system("cls"); // Mude para system("clear") se estiver no Linux!
121 |
122 | listaDePessoas[indice] = p; // Armazena os dados de p dentro do Array
123 |
124 | indice++;
125 | } while(indice < 5);
126 |
127 | // Estrutura de repetição simples para mostrar os dados de todas as pessoas do Array
128 | for (indice = 0; indice < 5; indice++) {
129 | printf("\n\nDados da pessoa %d:\n", indice+1);
130 | printf("Idade: %d\n", listaDePessoas[indice].idade);
131 | printf("Altura: %.2f\n", listaDePessoas[indice].altura);
132 | printf("Peso: %.2f\n", listaDePessoas[indice].peso);
133 | }
134 |
135 | return 0;
136 | }
137 | ```
138 | **Structs** são muito importantes pois na linguagem C é o que mais se assemelha a um Objeto de uma linguagem Orientada a Objetos. Se você entender bem sobre **Structs**, terá maior facilidade na Programação Orientada a Objetos.
139 |
140 | ## Sugestão
141 | Faça esse mesmo programa de cadastro de Pessoas, porém, adicione o campo ***nome*** no tipo de dado **Pessoa**. PS: Você terá que utilizar as funções da biblioteca **** para manipular cadeias de caracteres (Strings)
142 |
--------------------------------------------------------------------------------
/src/3-Basico/05-OperacoesAritmeticas.md:
--------------------------------------------------------------------------------
1 | # Operadores Aritmeticos
2 | Os operadores matemáticos são de importância fundamental para qualquer tipo de linguagem de programação. Eles são os operadores de adição, subtração, multiplicação, divisão, mod (resto da divisão), incremento e decremento.
3 |
4 |
5 | ## Operador de Adição
6 | Como o nome já diz, é um operador que realiza uma soma, ele pode ser atribuido dentro de variáveis ou realizando soma de duas variáveis, no exemplo abaixo fará mais sentido.
7 |
8 | ```c
9 | #include
10 |
11 | int main(void) {
12 |
13 | int soma = 5 + 2; //variável inteira de nome "soma" recebe o valor da soma entre 5 e 2;
14 |
15 | printf("O valor da soma 5 + 2 = %d\n", soma);
16 |
17 | //Também pode ser declarado como:
18 | int num1 = 5; //variavel inteira de nome "num1" recebe o valor de 5.
19 |
20 | int num2 = 2; //variavel inteira de nome "num2" recebe o valor de 2.
21 |
22 | int soma2 = num1 + num2; //variavel de nome "soma2" recebe o valor da soma entre as variaveis de nome "num1" e "num2"
23 |
24 | printf("O valor da soma %d + %d = %d\n", num1, num2, soma2);
25 |
26 | }
27 | ```
28 |
29 |
30 | ## Operador de subtração
31 |
32 | Como o nome já diz, é um operador que realiza uma subtração, ele pode ser atribuido dentro de variáveis ou realizando subtrações de duas variáveis, no exemplo abaixo fará mais sentido.
33 |
34 | ```c
35 | #include
36 |
37 | int main(void) {
38 |
39 | int subtracao = 10 - 4; //variável inteira de nome "subtracao" recebe o valor da subtracao entre 10 e 4;
40 |
41 | printf("O valor da subtração 10 - 4 = %d\n", subtracao);
42 |
43 | //Também pode ser declarado como:
44 | int num1 = 10; //variavel inteira de nome "num1" recebe o valor de 10.
45 |
46 | int num2 = 4; //variavel inteira de nome "num2" recebe o valor de 4.
47 |
48 | int subtracao2 = num1 - num2; //variavel de nome "subtracao2" recebe o valor da subtração entre as variaveis de nome "num1" e "num2"
49 |
50 | printf("O valor da subtracao %d - %d = %d\n", num1, num2, subtracao2);
51 |
52 | }
53 | ```
54 |
55 | ## Operador de multiplicação
56 |
57 | Como o nome já diz, é um operador que realiza uma multiplicação, ele pode ser atribuido dentro de variáveis ou realizando subtrações de duas variáveis, no exemplo abaixo fará mais sentido.
58 |
59 | ```c
60 | #include
61 |
62 | int main(void) {
63 |
64 | int multiplicacao = 10 * 5; //variável inteira de nome "multiplicacao" recebe o valor da multiplicação entre 10 e 5;
65 |
66 | printf("O valor da multiplicacao 10 * 5 = %d\n", multiplicacao);
67 |
68 | //Também pode ser declarado como:
69 | int num1 = 10; //variavel inteira de nome "num1" recebe o valor de 10.
70 |
71 | int num2 = 5; //variavel inteira de nome "num2" recebe o valor de 5.
72 |
73 | int multiplicacao2 = num1 * num2; //variavel de nome "multiplicacao2" recebe o valor da multiplicacao entre as variaveis de nome "num1" e "num2"
74 |
75 | printf("O valor da multiplicacao %d * %d = %d\n", num1, num2, multiplicacao2);
76 |
77 | }
78 | ```
79 |
80 | ## Operador de divisão
81 |
82 | Como o nome já diz, é um operador que realiza uma divisão, ele pode ser atribuido dentro de variáveis ou realizando subtrações de duas variáveis, no exemplo abaixo fará mais sentido.
83 |
84 | ```c
85 | #include
86 |
87 | int main(void) {
88 |
89 | int divisao = 10 / 5; //variável inteira de nome "divisao" recebe o valor da divisão entre 10 e 5;
90 |
91 | printf("O valor da divisão 10 / 5 = %d\n", divisao);
92 |
93 | //Também pode ser declarado como:
94 | int num1 = 10; //variavel inteira de nome "num1" recebe o valor de 10.
95 |
96 | int num2 = 5; //variavel inteira de nome "num2" recebe o valor de 5.
97 |
98 | int divisao2 = num1 / num2; //variavel de nome "divisao2" recebe o valor da divisao entre as variaveis de nome "num1" e "num2"
99 |
100 | printf("O valor da divisao %d / %d = %d\n", num1, num2, divisao2);
101 |
102 | }
103 | ```
104 |
105 | ## Operador de Mod
106 |
107 | Esse operador retorna o resto de uma divisão. O exemplo abaixo fica mais simples de entender.
108 |
109 | ```c
110 | #include
111 |
112 | int main(void) {
113 |
114 | int mod = 10 % 5; //variável inteira de nome "mod" recebe o valor do resto da divisão entre 10 e 5;
115 |
116 | printf("O resto da divisão 10 / 5 = %d\n", mod);
117 |
118 | //Também pode ser declarado como:
119 | int num1 = 10; //variavel inteira de nome "num1" recebe o valor de 10.
120 |
121 | int num2 = 5; //variavel inteira de nome "num2" recebe o valor de 5.
122 |
123 | int mod2 = num1 % num2; //variavel de nome "mod2" recebe o valor do resto da divisão entre as variaveis de nome "num1" e "num2"
124 |
125 | printf("O resto da divisão %d %% %d = %d\n", num1, num2, mod2);
126 |
127 | }
128 | ```
129 |
130 | **Obs**: Operações entre parênteses serão resolvidas primeiro, e depois de multiplicação, divisão e módulo.
131 | [Mais detalhes sobre ordem de precedência](https://en.cppreference.com/w/c/language/operator_precedence)
132 |
133 | ## Operações resumidas
134 |
135 | Se uma variável for subtrair uma variável com 1, pode-se fazer das seguintes maneiras:
136 |
137 | ```c
138 | int x = 10;
139 | printf("%d\n", x);
140 |
141 | x = x - 1; //funciona com qualquer valor, nao somente 1
142 | printf("%d\n", x);
143 |
144 | x -= 1; //funciona com qualquer valor nao somente 1
145 | printf("%d\n", x);
146 |
147 | x--; //subtrai somente 1 da variavel.
148 | printf("%d\n", x);
149 | ```
150 |
151 | Se uma variável for somar uma variável com 1, pode-se fazer das seguintes maneiras:
152 |
153 | ```c
154 | int x = 10;
155 | printf("%d\n", x);
156 |
157 | x = x + 1; //funciona com qualquer valor, nao somente 1
158 | printf("%d\n", x);
159 |
160 | x += 1; //funciona com qualquer valor nao somente 1
161 | printf("%d\n", x);
162 |
163 | x++; //soma somente 1 da variavel.
164 | printf("%d\n", x);
165 | ```
--------------------------------------------------------------------------------
/src/5-Avancado/3-Função-Recursiva.md:
--------------------------------------------------------------------------------
1 | # Função recursiva
2 |
3 |
4 |
5 |
6 |
7 | ### 1) Definição
8 | Em poucas palavras, uma **função recursiva é uma função que chama a si mesma**, tal qual uma mão desenhando a outra. A obra acima, _Desenhando mãos_ (1948), é de autoria do artista holandês M. C. Escher (1898-1972). Entre seus mais de 2 mil desenhos, o tema da recursão como forma de construir ilusões óticas é bastante recorrente.
9 |
10 | ### 2) Quando usar
11 | Em linguagem de programação, a recursão é uma ferramenta extremamente poderosa e sintética para resolver problemas menores que se repetem até que uma condição de encerramento seja atingida.
12 |
13 | > :warning: McDowell, no livro "Cracking the coding interview", diz que "todo algoritmo recursivo também pode ser implementado iterativamente [...]"
14 |
15 | Iterativamente significa _repetição_. Em termos práticos, isto significa utilizar uma estrutura de repetição, como um `for` ou um `while`, para resolver um problema. **Toda recursão pode ser "adaptada" para o uso de uma dessas duas estruturas de repetição**, conforme veremos no exemplo a seguir.
16 |
17 | Contudo, tenha em mente que haverá situações nas quais fará mais sentido utilizar uma função recursiva em vez de uma estrutura de repetição. Por exemplo, ao percorrer elementos listados em uma estrutura de dados chamada árvore (_tree_).
18 |
19 | ### 3) Como usar
20 |
21 | Uma função recursiva é composta de pelo menos duas partes:
22 | * um caso base: situação em que a chamada da função recursiva é interrompida
23 | * um caso recursivo: situação em que há a chamada da função recursiva para executar uma tarefa
24 |
25 | ### 4) Entendendo na prática
26 |
27 | #### 4.1) Implementação de uma função de potência de forma iterativa
28 |
29 | Na matemática, potenciação ou exponenciação é a operação que multiplica um número por ele mesmo várias vezes. Por exemplo, 2 elevado a 4 significa multiplicar 2 por ele mesmo quatro vezes (isto é, 2 * 2 * 2 * 2). O resultado desta multiplicação é 16.
30 |
31 | Aqui, o passo a passo para realizar esta conta é:
32 | * 2 * 2 = 4
33 | * 4 * 2 = 8
34 | * 8 * 2 = 16
35 |
36 | > :warning: Lembre-se da exceção na potenciação: todo número elevado a zero é 1, assim como 0 elevado a 0 resulta em 1. Nós trataremos esta exceção no nosso código.
37 |
38 | Perceba que a cada vez que realizamos a multiplicação, nós utilizamos o resultado desta como referência para a próxima conta. Assim, a nossa função receberá dois parâmetros: a **base**, isto é, o valor que queremos quero multiplicar, e o **expoente**, ou seja, por quantas vezes a base será multiplicada.
39 |
40 | De forma iterativa, podemos implementar a potenciação em linguagem C da seguinte maneira:
41 |
42 | ```c
43 | int iterative_power(int base, int expoente)
44 | {
45 | int resultado;
46 |
47 | resultado = base;
48 |
49 | // Condição para fazer nosso programa aceitar somente números positivos
50 | if (base < 0)
51 | return (0);
52 |
53 | // Condição para a exceção da exponenciação
54 | if (base == 0 || expoente == 0)
55 | return (1);
56 |
57 | // Realização da conta de potenciação
58 | while (--expoente)
59 | {
60 | result *= base;
61 | }
62 | return (resultado);
63 | }
64 | ```
65 |
66 | Enquanto o expoente for diferente de zero, a estrutura de repetição `while` seguirá realizando a conta de potenciação. Cada vez que a conta é feita, subtraimos o valor do expoente em 1. Na prática, o fluxo de execução para o caso 2 elevado a 4 é o seguinte:
67 |
68 | |Chamada do `while`| `resultado` | `expoente` | `base` |
69 | |:----|:---|:---|:-----|
70 | |1ª chamada|2 * 2 = `4`|4 - 1 = **`3`**| `2` |
71 | |2ª chamada|4 x 2 = `8`|3 - 1 = **`2`**| `2` |
72 | |3ª chamada|8 x 2 = `16`|2 - 1 = **`1`**| `2` |
73 |
74 | Na quarta chamada, o expoente será igual a zero (1 - 1 = 0). Consequentemente, o `while` é interrompido e a nossa função retornará `resultado = 16`.
75 |
76 | #### 4.2) Implementação de uma função de potência de forma recursiva
77 |
78 | Perceba que nesta implementação, o código é quase o mesmo. Porém substituímos o laço de repetição `while` por uma função recursiva. Leve o tempo que for necessário para entender o código a seguir, na seção 5 estará o código completo para você compilar em sua máquina local.
79 |
80 | ```c
81 | int recursive_power(int base, int expoente)
82 | {
83 | int resultado;
84 |
85 | resultado = base;
86 |
87 | // Tratamento de exceção
88 | if (base < 0)
89 | return (0);
90 |
91 | // Tratamento de exceção
92 | if (base == 0 || expoente == 0)
93 | return (1);
94 |
95 | // CASO RECURSIVO
96 | if (expoente > 1)
97 | resultado = base * ft_recursive_power(base, expoente - 1);
98 |
99 | // CASO BASE
100 | // expoente é igual a 0. Não há mais multiplicações a fazer,
101 | // então ele para a chamada de função recursiva e retorna
102 | // o valor final da multiplicação
103 | return (resultado);
104 | }
105 | ```
106 |
107 | Assim como na implementação iterativa, aqui também ocorrerá quatro chamadas. Porém, desta vez da nossa função recursiva. Enquanto o expoente for maior que 1, a função recursiva será chamada. Na prática, isto aqui acontece:
108 |
109 | ```c
110 | resultado = base * ft_recursive_power(base, 4 - 1) * ft_recursive_power(base, 3 - 1) * ft_recursive_power(base, 2 - 1) * ft_recursive_power(base, 1 - 1)
111 | ```
112 |
113 | Em outras palavras, é como se resolvessemos a expressão a matémática a seguir: `resultado = 2 * {2 * [2 * (2 * 1)]}`, onde a última recursão que caiu no caso base devolve o seu resultado para a função anterior que a chamou. Por sua vez, esta devolve o valor para a anterior e assim por diante até que todas as funções recursivas sejam resolvidas.
114 |
115 |
116 | ### 5) Código completo com `main` para estudos e testes
117 |
118 | Arquivo `main.c`
119 |
120 | ```c
121 | #include
122 |
123 | int iterative_power(int base, int expoente);
124 | int recursive_power(int base, int expoente);
125 |
126 | int main(void)
127 | {
128 | int base;
129 | int expoente;
130 |
131 | base = 2;
132 | expoente = 4;
133 | printf("O resultado é: %d\n", \
134 | iterative_power(base, expoente));
135 | printf("O resultado é: %d\n", \
136 | recursive_power(base, expoente));
137 | return (0);
138 | }
139 |
140 | int iterative_power(int base, int expoente)
141 | {
142 | int resultado;
143 |
144 | resultado = base;
145 |
146 | // Condição para fazer nosso programa aceitar somente números positivos
147 | if (base < 0)
148 | return (0);
149 |
150 | // Condição para a exceção da exponenciação
151 | if (base == 0 || expoente == 0)
152 | return (1);
153 |
154 | // Realização da conta de potenciação
155 | while (--expoente)
156 | {
157 | result *= base;
158 | }
159 | return (resultado);
160 | }
161 |
162 | int recursive_power(int base, int expoente)
163 | {
164 | int resultado;
165 |
166 | resultado = base;
167 |
168 | // Tratamento de exceção
169 | if (base < 0)
170 | return (0);
171 |
172 | // Tratamento de exceção
173 | if (base == 0 || expoente == 0)
174 | return (1);
175 |
176 | // CASO RECURSIVO
177 | if (expoente > 1)
178 | resultado = base * ft_recursive_power(base, expoente - 1);
179 |
180 | // CASO BASE
181 | // expoente é igual a 0. Não há mais multiplicações a fazer,
182 | // então ele para a chamada de função recursiva e retorna
183 | // o valor final da multiplicação
184 | return (resultado);
185 | }
186 | ```
187 |
188 | ### 6) Exercícios
189 |
190 | Implemente em C duas funções: `fatorial_iterativo()` e `fatorial_recursivo()`. Ambas as funções recebem um número inteiro como parâmetro e devolvem o seu fatorial. Contudo, a primeira função calculará o fatorial de forma iterativa e, a segunda, de forma recursiva. Se não souber o que é um fatorial, pesquise. Faz parte da proposta do exercício.
191 |
192 | ### 7) Conclusão
193 |
194 | Espero que tenha gostado deste tutorial sobre funções recursivas. Quaisquer dúvidas que você tiver, entre em contato comigo: [Ygor Sena](https://github.com/ygor-sena)
195 |
--------------------------------------------------------------------------------
/src/3-Basico/09-Controle-de-Fluxo.md:
--------------------------------------------------------------------------------
1 | # Controle de Fluxo
2 |
3 | Estruturas de Decisões são estruturas dentro da linguagem C que desviam os procedimentos do seu programa para uma **escolha**. Utilizamos muitas dessas estruturas quando queremos realizar testes, comparações ou redirecionar o nosso programa para outro caminho, dependendo da escolha do usuário. Existem **quatro** tipos de controle de fluxo, sendo elas:
4 |
5 | ## IF
6 | A estrutura de decisão IF *(**se**, em inglês)* pode ser utilizada para verificar qual número é o maior, por exemplo. Você sempre deve atribuir uma condição dentro das chaves do IF, para que a condição possa ser verdadeira ou falsa.
7 | ```c
8 | #include
9 |
10 | int main(void){
11 |
12 | /* Introduzimos o maior porque, conforme outros números superior a 0 apareçam,
13 | eles irão ser análisados pela estrutura de decisão. */
14 | int n1 = 10, n2 = 3, maior = 0;
15 |
16 | // Aqui estamos inserindo uma decisão, perguntando se a variável n1 é maior que a n2.
17 | if( n1 > n2 ){
18 | maior = n1;
19 |
20 | /* Se a variável for maior, o programa irá acessar as chaves do IF
21 | e ler o trecho do código que está dentro dele,
22 | neste caso, irá atribuir o valor da variável n1 para a variável maior. Então, maior começará a ser 10. */
23 | }
24 |
25 | // Aqui estamos exibindo na tela o maior número.
26 | printf ("%d\n", maior);
27 |
28 | return 0;
29 | }
30 | ```
31 | Utilizamos então o IF para verificar se o número 1 é maior que o número 2. No exemplo de cima, o fato é verdadeiro, porque o número 1 vale 10 e o número 2 vale 9.
32 |
33 | ### Saída
34 |
35 | ```c
36 | 10
37 | ```
38 |
39 | ## IF & ELSE
40 | A estrutura de decisão **IF** e **ELSE** *(**Caso contrário**, em inglês)* carregam decisões opostas nas estruturas de decisão. O programa irá ler primeiramente o IF, e caso ele seja falso, irá ler o ELSE. **Por obrigação, um ELSE sempre deve vir acompanhado e posterior a um IF**, mas um IF pode existir sem possuir um ELSE posterior a ele.
41 |
42 | ```c
43 | #include
44 |
45 | int main(void){
46 |
47 | // Estamos definindo a variável numero com o valor de 9.
48 | int numero = 9;
49 |
50 | if( numero > 10 ){
51 | printf ("O número é maior que 10.\n");
52 | /* Essa mensagem irá aparecer se a variável 'número' tiver
53 | o valor MAIOR que 10. */
54 | }else{
55 | printf ("O número não é maior que 10.\n");
56 | /* Essa mensagem irá aparecer se a variável 'número' tiver
57 | o valor MENOR que 10. */
58 | }
59 |
60 | return 0;
61 | }
62 | ```
63 |
64 | ### Saída
65 |
66 | ```c
67 | O número não é maior que 10.
68 | ```
69 |
70 | ## ELSE IF
71 | A estrutura de decisão **ELSE IF** *(**Caso contrário, se,** em inglês)*funciona igualmente a um IF, carregando uma condição entre os seus parenteses, diferentemente do ELSE, que caso o IF não seja verdadeiro, ele automaticamente é acessado.
72 | O **ELSE IF** deve ser utilizado obrigatoriamente: após um IF ser inserido, mas antes de um ELSE.
73 | Ele pode ser adicionado inúmeras vezes.
74 |
75 | ```c
76 | #include
77 |
78 | int main(void){
79 |
80 | // Aqui, definimos a variável número1 com o valor de 12.
81 | int numero1 = 12;
82 |
83 | // A condição aqui só pode ser acessada caso a variável número1 seja maior que 30, ou seja, 31,32,33...
84 | if( numero1 > 30 ){
85 | printf ("O número é maior que 30\n");
86 | /* Essa mensagem só irá aparecer caso o número seja maior que 30.
87 | Como não é, não entramos nessa condição, e respectivamente, essa mensagem não aparece. */
88 | }
89 | else if ( numero1 > 15 ){
90 | printf ("O número é maior que 15 e menor que 30.\n");
91 | }
92 | // A condição aqui é se a variável numero1 contém o valor maior que 10.
93 | else if ( numero1 > 10 ){
94 | printf ("O número é maior que 10 e menor que 15.\n");
95 | /* Como o valor da variável numero1 é 12,
96 | então acessamos esse ELSE IF e essa mensagem é exibida na tela do usuário. */
97 | }
98 | /* Como a nossa variável já achou uma condição acessível, no caso o ELSE IF,
99 | esse ELSE não será lido. */
100 | else {
101 | printf ("O número é menor que 10\n");
102 | }
103 |
104 | return 0;
105 | }
106 | ```
107 | ### Saída
108 |
109 | ```c
110 | O número é maior que 10 e menor que 15.
111 | ```
112 |
113 | ## OPERADOR TERNÁRIO
114 | O **operador ternário** é uma forma 'resumida' de escrever um IF e um ELSE. Esse comando é utilizado numa simples linha, deixando o programa limpo visualmente, porque ao invés de escrevermos inúmeras chaves e condições para o *IF, ELSE IF e ELSE*, utilizamos:
115 |
116 | ```c
117 | condição ? se a condição é verdadeira : se a condição é falsa;
118 | ```
119 | Ou seja, daremos uma condição e introduziremos um sinal de interrogação (?) após a condição ser apresentada. Esse sinal de interrogação funciona como um IF.
120 | **Se a condição for verdadeira**, o bloco de código após o sinal de interrogação vai ser realizado. **Caso a condição seja falsa**, o bloco de código após os dois pontos será realizado.
121 | Recomendamos a utilização do operador ternário quando queremos verificar algo simples, que custará apenas uma linha de código.
122 | Por exemplo:
123 | ```c
124 | #include
125 |
126 | int main(void){
127 |
128 | int salario = 2000;
129 | salario > 1200 ? printf ("O salário é superior a 1200.\n") : printf ("O salário é inferior a 1200.\n");
130 |
131 | /* Estamos colocando a condição perguntando se o salário é maior que 1200
132 | Se for superior, o 'printf' após o sinal de interrogação será exibido para o usuário
133 | Caso contrário, o 'printf' após os dois pontos será exibido para o usuário.
134 |
135 | Notem que o primeiro printf não possui ponto e vírgula (;) no final, isso é padrão na operação ternária.
136 | */
137 |
138 | return 0;
139 | }
140 | ```
141 | ### Saída
142 | ```c
143 | O salário é superior a 1200.
144 | ```
145 |
146 | O operador ternário também serve para retornar valores e para atribuir valores.
147 | ```c
148 | #include
149 |
150 | int main(void){
151 |
152 | int numero = 10;
153 |
154 | numero >= 0 ? numero = numero + 1 : numero = numero - 1;
155 | printf ("O novo número é %d\n");
156 | }
157 | ```
158 |
159 | ### Saída
160 |
161 | ```c
162 | 11.
163 | ```
164 |
165 | ## SWITCH
166 |
167 | O comando **switch** *(**trocar**, em inglês)* é uma forma de reduzir a complexidade de vários IF & ELSE encadeados. O conteúdo de uma variável é comparado com um valor constante, e caso a comparação seja verdadeira, um determinado comando é executado.
168 | Os **case** *(**caso**, em inglês)* são as condições que o nosso **switch** irá acessar caso seja verdadeira. Caso a variável tenha o valor '1', o **case 1** será acessado. Caso a variável tenha o valor '2', o **case 2** será acessado, e assim por diante.
169 | Todo **case** é acompanhado com um **break** dentro dele. Caso não haja um break, o programa irá entrar num **case** e não irá realizar mais nenhuma ação.
170 |
171 | ```c
172 | #include
173 |
174 | int main (void)
175 | {
176 | int valor;
177 |
178 | printf ("Digite um valor de 1 a 7: "); // Imagine que o usuário digite 2.
179 | scanf ("%d", &valor);
180 |
181 | switch ( valor )
182 | {
183 | case 1 :
184 | printf ("Domingo\n");
185 | break;
186 |
187 | case 2 :
188 | printf ("Segunda\n");
189 | break;
190 |
191 | case 3 :
192 | printf ("Terça\n");
193 | break;
194 |
195 | case 4 :
196 | printf ("Quarta\n");
197 | break;
198 |
199 | case 5 :
200 | printf ("Quinta\n");
201 | break;
202 |
203 | case 6 :
204 | printf ("Sexta\n");
205 | break;
206 |
207 | case 7 :
208 | printf ("Sabado\n");
209 | break;
210 |
211 | //Se nenhum dos casos acima é verdadeiro, o default entra em ação
212 | default :
213 | printf ("Valor invalido!\n");
214 | }
215 |
216 | return 0;
217 | }
218 | ```
219 |
220 | ### Saída:
221 |
222 | ```c
223 | Segunda
224 | ```
225 |
226 | ## BREAK
227 |
228 | Na linguagem C o **break** *(**parar**, em inglês)* serve para dois usos, o primeiro é **forçar a saída** do **case** que o **switch** possui, assim como no exemplo a cima e para forçar a saída de um laço de repetição.
229 |
230 | Se mais de uma condição for válida, é possivel unir os **case**, como se fosse um **IF** com várias condições. Só que de uma forma diferente e bem melhor de ser entendida.
231 | ### Exemplo:
232 | ```c
233 | #include
234 |
235 | int main (void)
236 | {
237 | int valor;
238 |
239 | printf ("Digite um valor de 1 a 7: ");
240 | scanf ("%d", &valor);
241 |
242 | switch ( valor )
243 | {
244 | case 1 :
245 | case 7 :
246 | printf ("Final de semana\n");
247 | break;
248 |
249 | case 2 :
250 | case 3 :
251 | case 4 :
252 | case 5 :
253 | case 6 :
254 | printf ("Dia da semana\n");
255 | break;
256 |
257 | //Se nenhum dos casos acima é verdadeiro, o default entra em ação
258 | default :
259 | printf ("Valor invalido!\n");
260 | }
261 |
262 | return 0;
263 | }
264 | ```
--------------------------------------------------------------------------------
/.github/id_4noobs_compact.svg:
--------------------------------------------------------------------------------
1 |
2 |
140 |
--------------------------------------------------------------------------------
/.github/id_4noobs_line.svg:
--------------------------------------------------------------------------------
1 |
2 |
140 |
--------------------------------------------------------------------------------
/.github/id_4noobs_expanded.svg:
--------------------------------------------------------------------------------
1 |
2 |
142 |
--------------------------------------------------------------------------------
/src/5-Avancado/5-Threads.md:
--------------------------------------------------------------------------------
1 | # Programação Multithread
2 |
3 |
4 |
5 | ### 1) Definição
6 | **Threads** são tarefas (tasks) que concorrem simultaneamente para execução na CPU. Isso significa que o programa pode realizar várias operações ao mesmo tempo. As threads ocupam espaço na memória do programa, tendo sua própria pilha (stack) e compartilhando todas as variáveis globais disponíveis no programa. Essas variáveis compartilhadas são chamadas de **seções críticas**.
7 |
8 | A função **main** e todo o seu programa são executados por uma única thread. Quando não utilizamos programação multithread, temos apenas uma linha de execução, representada pela **thread main**. Como as threads estão dentro da área de memória do programa, se a main for finalizada, **TODAS** as demais threads também serão finalizadas, pois o kernel irá desalocar o programa da memória.
9 |
10 | ### 2) Como usar
11 |
12 | Para utilizarmos a programação multithread, precisamos de uma biblioteca implementada para o sistema operacional, que permita o uso de threads. Em C, a biblioteca utilizada é a **pthreads** utilizando o **#include **. Ela está disponível para Linux, macOS e Windows.
13 |
14 | Para criar uma thread, utilizamos o tipo **pthread_t** no código, o que nos permite instanciar uma thread que pode ser inicializada ou configurada conforme necessário. Mais adiante, veremos como alterar algumas dessas configurações. Para inicializar a thread, utilizamos a função **pthread_create**, cujo protótipo é mostrado abaixo:
15 |
16 | ```c
17 | int pthread_create(pthread_t *restrict thread,
18 | const pthread_attr_t *restrict attr,
19 | void *(*start_routine)(void *),
20 | void *restrict arg);
21 |
22 | /*
23 | A função irá retornar 0 se tiver sucesso,
24 | caso contrario um valor do erro ocorrido
25 | */
26 | ```
27 |
28 | O primeiro parâmetro `*restrict thread` é um ponteiro para variável `pthread_t`.
29 |
30 | O segundo parâmetro `*restrict attr` é ponteiro para variável do tipo `pthread_attr_t`, que serve para configurar algumas propriedades da thread. Se quisermos usar as configurações padrão, podemos passar **NULL** nesse parâmetro.
31 |
32 | O próximo parâmetro, `*(*start_routine)(void *)`, é um ponteiro para a **função** que queremos que a thread inicie.
33 |
34 | Por fim, o parâmetro `*restrict arg` é um ponteiro para a variável que será utilizada como **parâmetro** da função que será executada, caso não exista parâmetros, podemos passar **NULL** nesse parâmetro.
35 |
36 | Abaixo podemos ver um código de exemplo:
37 |
38 | ```c
39 | #include
40 | #include
41 |
42 | void *thread_run()
43 | {
44 | printf("Ola mundo !!\nEu sou a Thread que acabou de nascer\n\n");
45 | return NULL;
46 | }
47 |
48 | int main()
49 | {
50 | pthread_t thread;
51 |
52 | printf("Main criando e iniciando a Thread\n\n");
53 | pthread_create(&thread, NULL, thread_run, NULL);
54 | /*
55 | Execute esse programa com a linha abaixo comentada
56 | para observar alguns efeitos que ocorrem quando não existe
57 | a sincronização das Threads.
58 | */
59 | pthread_join(thread, NULL);
60 |
61 | printf("Main encerrando\n");
62 | return 0;
63 | }
64 | ```
65 | No exemplo acima, utilizamos a função `pthread_join()`. Essa função faz com que a `main` (ou qualquer outra thread) espere pela finalização da thread passada como argumento. Isso significa que a execução da `main` é interrompida até que a thread termine completamente a execução da função thread_run. Isso é necessário porque, como vimos, se a thread `main` finalizar ou morrer, todas as outras threads também serão finalizadas, interrompendo seu progresso de forma prematura.
66 |
67 | #### 2.1) Retornos e Parâmetros
68 |
69 | Como vimos, a função de execução da thread deve utilizar tipos **void**. Assim, podemos usar o `cast` para enviar os parâmetros como **void\*** e, dentro da função, fazer o `cast` para o tipo desejado que queremos manipular.
70 |
71 | Abaixo, utilizando um exemplo semelhante ao anterior, iremos enviar e receber dados da nossa thread. Neste exemplo, enviaremos um int, e a thread retornará o valor multiplicado por 10. Como o retorno deve ser um ponteiro, não podemos retornar uma variável local da função, pois essa será removida da *stack* após o término da função. Para resolver isso, alocaremos memória dinamicamente para um int, armazenaremos o valor multiplicado por 10 e, em seguida, retornaremos esse ponteiro.
72 |
73 | A função `pthread_join`, além de bloquear uma thread e esperar pela finalização de outra, também tem a capacidade de receber o retorno (um ponteiro para void*) da thread finalizada. No segundo parâmetro, passaremos um ponteiro para int, para que ele possa receber e armazenar o endereço com o resultado calculado na thread.
74 |
75 | ```c
76 | #include
77 | #include
78 | #include
79 |
80 | void *thread_run(void *num)
81 | {
82 | int *aux = (int *)malloc(sizeof(int));
83 | *aux = *(int *)num;
84 | *aux *= 10;
85 | printf("Vou retornar 10 vezes o meu ID\n\n");
86 | return aux;
87 | }
88 |
89 | int main()
90 | {
91 | pthread_t thread;
92 | int thread_id = 5;
93 | int *thread_return;
94 |
95 | printf("Main criando e iniciando a Thread\n\n");
96 | pthread_create(&thread, NULL, thread_run, (void *)&thread_id);
97 |
98 | pthread_join(thread, (void **)&thread_return);
99 | printf("Thread retornou %d\n\n", *thread_return);
100 |
101 | printf("Main encerrando\n");
102 |
103 | return 0;
104 | }
105 | /*
106 | Algumas boas práticas de programação foram
107 | removidas para manter o código simples e mais legível.
108 | No entanto, é importante verificar o
109 | retorno da função pthread_create e assegurar que a
110 | alocação dinâmica foi bem-sucedida e utilizar free
111 | para liberar a memória alocada.
112 | */
113 | ```
114 |
115 | #### 2.2) Atributos de uma Thread
116 |
117 | A tabela abaixo mostra alguns atributos que uma thread pode receber. Se você está começando seus estudos sobre threads, pode pular o tópico 2.1. Para aprender, é possível utilizar normalmente as threads com seus atributos configurados nos valores padrão (default)
118 |
119 | | `Atributo` | `Função` | `Valor Padrão` | `Resultado` |
120 | | :--: | :---: | :---: | :--: |
121 | | scope | pthread_attr_setscope() | PTHREAD_SCOPE_PROCESS | Thread está desvinculada da LWP. |
122 | | detachstate | pthread_attr_setdetachstate() | PTHREAD_CREATE_JOINABLE | O status de saída e a thread são preservados após o término do thread. |
123 | | stackaddr | pthread_attr_setstackaddr() | NULL | Thread possui endereço de pilha alocado pelo sistema. |
124 | | stacksize | pthread_attr_setstacksize() | 1 megabyte | Thread tem tamanho de pilha definido pelo sistema. |
125 | | priority | pthread_attr_setschedparam() | | Thread herda a prioridade da thread mãe. |
126 | | inheritsched | pthread_attr_setinheritsched() | PTHREAD_INHERIT_SCHED | Thread herda a prioridade de agendamento da thread mãe. |
127 | | schedpolicy | pthread_attr_setschedpolicy() | SCHED_OTHER | Thread utiliza escalonamento de prioridade fixa definido pelo Solaris; as threads executam até serem preemptadas por uma thread de maior prioridade, ou até bloquearem ou cederem voluntariamente a execução.. |
128 |
129 | Os atributos mencionados acima também podem ser obtidos através das funções **get**. Por exemplo, para a função `pthread_attr_setscope()`, existe a correspondente `pthread_attr_getscope()`. Todas as funções mencionadas possuem a sua respectiva versão *get*
130 |
131 | Em todos os casos, utilizar **NULL** na função `pthread_create()` é equivalente a passar um atributo que foi inicializado sem modificações, através da função `pthread_attr_init()`. Essa função inicializa o atributo com todos os valores padrão.
132 |
133 | ### 3) Problemas com Threads
134 | Como elas operam em simultaneidade em operações não atômicas isto é operações que podem ser interrompidas pela troca de contexto (programas) na CPU isto recebe o nome de preempção, nas quais voltam depois de um tempo ao processo, nesse meio tempo pode acontecer de outra thread executar e modificar os dados dessa operação não finalizada pela outra gerando inconsistências de dados e até mesmo o bloqueio de recurso, fazendo com que o programa entre em congelamento (deadlock), ficando uma thread a espera de outras e vice versa, fazendo com que nunca avancem do estado de aguardar, pra isso é necessário sincronização !!
135 |
136 | ### 4) Sincronização entre Threads
137 |
138 | Embora existam vantagens em utilizar threads, muitos problemas podem ocorrer quando não são tomadas as devidas precauções e métodos adequados para operá-las. Um dos problemas comuns é a **condição de corrida** entre threads que compartilham recursos que não podem ser acessados simultaneamente. Para evitar isso, é necessária a sincronização do acesso, uso e liberação desses recursos, a fim de evitar *deadlock*, que ocorre quando as threads entram em um estado de bloqueio infinito. Outro problema é o acesso de múltiplas threads a regiões de memória e funções compartilhadas, criando o que chamamos de **seção crítica**. Isso pode ocorrer em variáveis ou funções que utilizam variáveis estáticas em sua implementação. Devido à preempção, há o risco de dados serem corrompidos ou da falta de sincronização levar a uma execução incorreta da lógica do programa.
139 |
140 | #### 4.1) Mutex
141 |
142 | Para resolver esses problemas, precisamos de travas que sejam executadas de forma atômica, ou seja, que não possam ser interrompidas durante o processo de travamento ou liberação. Essas travas garantem que as threads executem de forma coesa e também permitem que apenas uma thread permaneça em execução dentro de um trecho de código. A thread pode sofrer preempção, mas isso não afeta a sincronização, pois, enquanto a trava não for liberada, a thread será a única na **seção crítica**. Na implementação da POSIX (pthreads), essa trava é chamada de **mutex**
143 |
144 | Para instanciar um mutex, utilizamos o tipo `pthread_mutex_t`, que deve ser inicializado posteriormente com a função `pthread_mutex_init()`. O protótipo da função está mostrado abaixo.
145 |
146 | ```c
147 | int pthread_mutex_init(pthread_mutex_t *restrict mutex,
148 | const pthread_mutexattr_t *restrict attr);
149 | ```
150 |
151 | Assim como as threads, os mutexes também possuem seus atributos. Se você deseja trabalhar com os valores padrão, pode utilizar **NULL** como segundo parâmetro na inicialização ou usar a constante `PTHREAD_MUTEX_INITIALIZER`. Como o mutex precisa ser acessado dentro da função da thread e geralmente é instanciado em outra função, é comum que eles sejam declarados como variáveis globais no programa.
152 |
153 | Abaixo, temos um código que utiliza mutex. Neste caso, várias threads incrementam uma variável. Se não houver sincronização nessa seção crítica, podem ocorrer inconsistências devido à preempção em operações que não são atômicas.
154 |
155 | ```c
156 | #include
157 | #include
158 |
159 | #define NUM_ITERATIONS 10000000
160 | #define NUM_THREADS 5
161 |
162 | /* Variável Critica, acesso simultâneo */
163 | unsigned long counter = 0;
164 |
165 | /* Mutex para sincronização das Threads */
166 | pthread_mutex_t mutex;
167 |
168 | void *increment_counter()
169 | {
170 | unsigned long i;
171 | for (i = 0; i < NUM_ITERATIONS; i++)
172 | {
173 | /* Trava a seção critica */
174 | pthread_mutex_lock(&mutex); /* Comente para observar anomalias */
175 | counter++;
176 | /* Seção critica livre, mutex liberada */
177 | pthread_mutex_unlock(&mutex);
178 | }
179 | return NULL;
180 | }
181 |
182 | int main()
183 | {
184 | size_t i;
185 | pthread_t threads[NUM_THREADS];
186 |
187 | /* Inicializa a mutex */
188 | pthread_mutex_init(&mutex, NULL);
189 | /* Equivalente a "mutex = PTHREAD_MUTEX_INITIALIZER;" */
190 |
191 | /* Inicia threads e a condição de corrida entre elas */
192 | for (i = 0; i < NUM_THREADS; i++)
193 | pthread_create(threads + i, NULL, increment_counter, NULL);
194 |
195 | /* Aguarda finalização da threads sem receber retornos */
196 | for (i = 0; i < NUM_THREADS; i++)
197 | pthread_join(threads[i], NULL);
198 |
199 | /* Imprime resultado da variável global (seção critica) */
200 | printf("Valor final do contador: %lu\n", counter);
201 |
202 | /* Destrói a mutex */
203 | pthread_mutex_destroy(&mutex);
204 |
205 | return 0;
206 | }
207 | ```
208 |
209 | Como podemos observar no código acima, as funções `pthread_mutex_lock` e `pthread_mutex_unlock` são responsáveis por gerenciar o mutex, travando a seção crítica e liberando-a após o término do processo da thread. O **lock** tenta adquirir e travar a seção crítica. Se a seção já estiver travada, a thread que tentar entrar ficará em estado de espera, aguardando a liberação pelo escalonador do kernel (ou pelo processo implementado no sistema operacional). Quando ocorre o **unlock**, as threads em espera são despertadas e tentam novamente adquirir a seção crítica. Com isso, mantemos a sincronização e a consistência dos dados compartilhados.
210 |
211 | No entanto, em condições de corrida, pode ocorrer starvation (fome), que acontece quando uma thread sempre perde na disputa pelo lock. Por exemplo, se uma thread conseguir executar rapidamente e retornar ao lock, ela poderá monopolizar a seção crítica até que o laço do `for` termine, fazendo com que a outra thread permaneça parada e sem progresso até que a primeira termine
212 |
213 |
214 | ### 5) Conclusão
215 |
216 | Espero que você tenha gostado deste tutorial sobre programação multithread. Se tiver alguma dúvida, não hesite em entrar em contato comigo: [JulioRats](https://github.com/Julio-Rats)
217 |
--------------------------------------------------------------------------------
/.github/header-4noobs.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
171 |
--------------------------------------------------------------------------------
/src/5-Avancado/4-ImplementacaoEdGenerica.md:
--------------------------------------------------------------------------------
1 | # Implementando estruturas de dados genêricas em C
2 |
3 | Neste Módulo, vamos aprender como implementar estruturas de dados genéricas em C, usando ponteiros para void e funções de comparação e liberação. Como exemplo vamos abordar a implementação de uma Hash Table, utilizando a técnica de encadeamento separado para lidar com colisões. A implementação completa está disponível em: [hsc hash separate chaining](https://github.com/hscHeric/hsc_hash_separate-chaining)
4 |
5 | ### 1) Motivação
6 | As estruturas de dados são fundamentais para organizar e manipular informações de forma eficiente. No entanto, muitas vezes precisamos lidar com dados de tipos diferentes, como inteiros, strings, structs, etc. Como podemos criar estruturas de dados que possam armazenar qualquer tipo de dado, sem precisar reescrever o código para cada tipo?
7 |
8 | A solução é usar estruturas de dados genéricas, que usam ponteiros para void para armazenar os dados, e funções de comparação e liberação para definir como os dados devem ser comparados e liberados da memória. Assim, podemos reutilizar o mesmo código para diferentes tipos de dados, bastando passar as funções adequadas como parâmetros.
9 |
10 | ### 3) Objetivos
11 | - Aprender o conceito de estruturas de dados genéricas e suas vantagens e desvantagens
12 | - Implementar uma tabela hash genêrica que aceite diferentes tipos de dados
13 |
14 | ### 4) Estruturas de dados
15 | As estruturas de dados utilizadas são duas:
16 |
17 | #### node_t
18 |
19 | ```c
20 | typedef struct node_t {
21 | void *key; // Ponteiro para a chave (geralmente um ponteiro para algum tipo de dado)
22 | size_t key_len; // Tamanho da chave em bytes
23 |
24 | void *value; // Ponteiro para o valor associado à chave
25 | size_t value_len; // Tamanho do valor em bytes
26 |
27 | struct node_t *next; // Ponteiro para o próximo nó na lista (encadeamento para implementar uma estrutura de dados como lista ligada)
28 | } node_t;
29 |
30 | ```
31 |
32 | A estrutura node_t representa um nó na tabela de hash com encadeamento separado. Ela foi projetada com flexibilidade para permitir o uso de qualquer tipo de dado como chave e valor, graças ao uso de ponteiros void *. Essa abordagem permite que diferentes tipos de dados, incluindo tipos personalizados, sejam usados como chave e valor na tabela de hash.
33 |
34 | Esta flexibilidade é proporcionada pela utilização de ponteiros void * para os campos key (chave) e value (valor). Isso significa que qualquer tipo de dado da linguagem C pode ser utilizado como chave e valor na tabela de hash. Por exemplo, é possível empregar tipos básicos de dados (como inteiros, caracteres, etc.) ou até mesmo tipos de dados personalizados definidos pelo usuário.
35 |
36 | #### hash_tab_t
37 |
38 | A estrutura hash_tab_t é a estrutura principal que representa a tabela de hash com encadeamento separado. Essa estrutura foi projetada para oferecer flexibilidade e eficiência no armazenamento e recuperação de dados, permitindo o uso de qualquer tipo de dado como chave e valor.
39 |
40 | ```c
41 | typedef struct hash_tab_t {
42 | node_t **buckets; // Ponteiro para um array de ponteiros de nós (representa os baldes ou buckets da tabela de hash)
43 | size_t size; // Número total de buckets na tabela de hash
44 | int count; // Número total de elementos armazenados na tabela de hash
45 | int (*hash_func)(void *, size_t, size_t); // Ponteiro para a função de hash utilizada para calcular o índice do bucket
46 | } hash_tab_t;
47 |
48 | ```
49 |
50 | - `node_t **buckets:` É um vetor de ponteiros para nós (node_t) que serve como os buckets (compartimentos) da tabela de hash. Cada bucket armazena um ponteiro para o primeiro nó de uma lista encadeada, permitindo a manipulação eficiente de colisões.
51 |
52 | - `size_t size:` Indica o tamanho total da tabela de hash, determinando o número de buckets disponíveis para distribuir os elementos.
53 |
54 | - `int count:` Representa o número atual de elementos armazenados na tabela de hash. Esse valor é atualizado dinamicamente durante a inserção e remoção de elementos.
55 |
56 | - `int (*hash_func) (void *, size_t, size_t):` É um ponteiro para uma função de hash personalizada ou a função de hash padrão. Essa função é responsável por calcular o índice onde um elemento será armazenado na tabela de hash, com base na chave fornecida. A flexibilidade dessa função permite a adaptação da tabela de hash para diferentes tipos de dados e tamanhos de tabela.
57 |
58 | ### Funções de inserção e remoção de dados genêricos
59 | Vamos focar em como adicionar e remover coisas de uma tabela de hash. Queremos que essa tabela seja capaz de lidar com diferentes tipos de informações, como números, palavras ou qualquer outra coisa. Vamos criar maneiras de colocar coisas nessa tabela (inserir) e tirar coisas dela (remover), e queremos que isso funcione para diferentes tipos de dados. Usaremos truques como ponteiros para funções e uma estrutura chamada node_t que pode armazenar diferentes tipos de informações. Isso nos permite construir uma tabela de hash que pode ser usada de maneira flexível para armazenar e recuperar informações, não importa que tipo elas sejam. Ao fazer isso, estamos criando ferramentas que podem ser usadas de muitas formas diferentes e são úteis para resolver diferentes tipos de problemas
60 | Mais especificamente vamos analizar as seguintes funções:
61 |
62 | #### Inicialização
63 | ```c
64 | /**
65 | * @brief Inicializa uma tabela de hash.
66 | *
67 | * @param size Tamanho da tabela.
68 | * @param hash_func Função de hash, se NULL, usa a função de hash padrão.
69 | * @return hash_tab_t* Ponteiro para a tabela de hash.
70 | */
71 | hash_tab_t *hash_tab_init(size_t size, int (*hash_func)(void *key, size_t keylen, size_t hash_tab_size));
72 |
73 | /**
74 | * @brief Busca um nó na tabela de hash.
75 | *
76 | * @param hash_table Tabela de hash.
77 | * @param key Chave do nó.
78 | * @param key_len Tamanho da chave.
79 | * @return void* Ponteiro para o valor do nó.
80 | */
81 | void *hash_tab_search(hash_tab_t *hash_table, void *key, size_t key_len);
82 |
83 | /**
84 | * @brief Insere um nó na tabela de hash.
85 | *
86 | * @param hash_table Tabela de hash.
87 | * @param key Chave do nó.
88 | * @param key_len Tamanho da chave.
89 | * @param value Valor do nó.
90 | * @param value_len Tamanho do valor.
91 | * @return void* Ponteiro para o valor do nó.
92 | */
93 | void *hash_tab_insert(hash_tab_t *hash_table, void *key, size_t key_len, void *value, size_t value_len);
94 |
95 | /**
96 | * @brief Remover um nó da tabela de hash.
97 | *
98 | * @param hash_table Tabela de hash.
99 | * @param key Chave do nó.
100 | * @param key_len Tamanho da chave.
101 | */
102 | void hash_tab_remove(hash_tab_t *hash_table, void *key, size_t key_len);
103 | ```
104 |
105 | #### hash_tab_init
106 | A função hash_tab_init é responsável por criar e inicializar uma tabela de hash para armazenar elementos, sendo uma operação fundamental para utilizar a estrutura de dados de tabela de hash oferecida neste projeto.
107 |
108 |
109 | ```c
110 | //Assinatura da função: hash_tab_t *hash_tab_init(size_t size, int (*hash_func)(void *key, size_t keylen, size_t hash_tab_size));
111 |
112 |
113 | hash_tab_t *ht = hash_tab_init(100, NULL);
114 | if (ht == NULL) {
115 | printf("Erro ao inicializar a tabela de hash!\n");
116 | // Lidar com o erro, se necessário
117 | }
118 | ```
119 | A Implementação é a seguinte:
120 | ```c
121 | /**
122 | * Função de hash padrão para converter uma chave em um índice de bucket.
123 | * @param key Ponteiro para a chave.
124 | * @param key_len Tamanho da chave em bytes.
125 | * @param hash_tab_size Tamanho total da tabela de hash (número de buckets).
126 | * @return Índice do bucket calculado.
127 | */
128 | static int default_hash_func(void* key, size_t key_len, size_t hash_tab_size){
129 | int sum = 0;
130 | for(int i = 0; i < (int) key_len; i++){
131 | sum += ((unsigned char *) key)[i] * (i + 1); // (i + 1) para evitar colisões entre palavras que são anagramas
132 | }
133 |
134 | return (sum % (int)hash_tab_size); // Retorna o resto da divisão da soma dos caracteres pela quantidade de buckets
135 | }
136 |
137 | /**
138 | * Inicializa uma tabela de hash.
139 | * @param size Tamanho total da tabela de hash (número de buckets).
140 | * @param hash_func Função de hash para calcular o índice do bucket.
141 | * @return Ponteiro para a tabela de hash inicializada ou NULL em caso de falha na alocação de memória.
142 | */
143 | hash_tab_t *hash_tab_init(size_t size, int (*hash_func)(void *key, size_t key_len, size_t hash_tab_size)) {
144 | hash_tab_t *hash_tab = (hash_tab_t *)malloc(sizeof(hash_tab_t));
145 | if(hash_tab == NULL) {
146 | return NULL; // Erro ao alocar memória
147 | }
148 |
149 | hash_tab->buckets = (node_t**)malloc(sizeof(node_t*) * size);
150 | if(hash_tab->buckets == NULL) {
151 | free(hash_tab);
152 | return NULL; // Erro ao alocar memória
153 | }
154 |
155 | hash_tab->size = size;
156 | hash_tab->count = 0;
157 |
158 | // Inicializa os buckets com NULL
159 | for (int i = 0; i < (int) size; i++) {
160 | hash_tab->buckets[i] = NULL;
161 | }
162 |
163 | // Define a função de hash padrão se não fornecida
164 | if(hash_func == NULL){
165 | hash_tab->hash_func = &default_hash_func;
166 | } else {
167 | hash_tab->hash_func = hash_func;
168 | }
169 |
170 | return hash_tab;
171 | }
172 | ```
173 |
174 | A função default_hash_func é responsável por converter uma chave em um índice de bucket em uma tabela de hash. Ela opera sobre os bytes da chave, multiplicando cada byte por sua posição (incrementada por 1) para evitar colisões entre palavras que são anagramas. O índice do bucket é calculado como o resto da divisão da soma desses valores pela quantidade total de buckets na tabela de hash.
175 |
176 | A função hash_tab_init cria e inicializa uma tabela de hash. Ela recebe o tamanho desejado da tabela (size) e uma função de hash opcional (hash_func). A alocação de memória é realizada para a estrutura hash_tab_t e para o array de ponteiros para nós (buckets). Os buckets são inicializados com valores nulos, e a função de hash padrão (default_hash_func) é utilizada se nenhuma função de hash for fornecida. A função retorna um ponteiro para a tabela de hash inicializada ou NULL em caso de falha na alocação de memória.
177 |
178 | Essas funções são projetadas para serem genéricas, permitindo a manipulação de pares chave-valor de diferentes tipos de dados. Elas podem ser usadas como base para construir uma estrutura de dados flexível, capaz de armazenar e recuperar informações independentemente do tipo subjacente. Ao fornecer uma função de hash personalizada, é possível adaptar a tabela de hash para requisitos específicos da aplicação.
179 |
180 | #### Inserção
181 | ```c
182 | /**
183 | * Insere um par chave-valor em uma tabela de hash, permitindo o uso de estruturas genéricas.
184 | * @param hash_table Ponteiro para a tabela de hash onde inserir o par chave-valor.
185 | * @param key Ponteiro para a chave a ser inserida.
186 | * @param key_len Tamanho da chave em bytes.
187 | * @param value Ponteiro para o valor a ser inserido.
188 | * @param value_len Tamanho do valor em bytes.
189 | * @return Ponteiro para o valor inserido na tabela de hash ou NULL em caso de falha na alocação de memória.
190 | */
191 | void *hash_tab_insert(hash_tab_t *hash_table, void *key, size_t key_len, void *value, size_t value_len) {
192 | // Calcula o índice do bucket usando a função de hash
193 | int index = default_hash_func(key, key_len, hash_table->size);
194 |
195 | node_t *next_node, *last_node;
196 | next_node = hash_table->buckets[index];
197 | last_node = NULL;
198 |
199 | // Procura se já existe um nó com a mesma chave
200 | while (next_node != NULL) {
201 | if (next_node->key_len == key_len) {
202 | // Se já existe um nó com a mesma chave, atualiza o valor
203 | if (memcmp(key, next_node->key, key_len) == 0) {
204 | if (next_node->value_len != value_len) {
205 | // Se o tamanho do valor é diferente, realoca a memória
206 | free(next_node->value);
207 | next_node->value = malloc(value_len);
208 | if (next_node->value == NULL) {
209 | return NULL; // Erro ao alocar memória para o novo valor
210 | }
211 | }
212 | // Copia o novo valor para o nó existente
213 | memcpy(next_node->value, value, value_len);
214 | next_node->value_len = value_len;
215 | return next_node->value; // Retorna o ponteiro para o valor atualizado
216 | }
217 | }
218 | last_node = next_node;
219 | next_node = next_node->next;
220 | }
221 |
222 | // Se não existe um nó com a mesma chave, cria um novo nó
223 | node_t *p_node = (node_t *)malloc(sizeof(node_t));
224 | if(p_node == NULL) {
225 | return NULL; // Erro ao alocar memória para o novo nó
226 | }
227 |
228 | // Aloca memória para a chave e o valor
229 | p_node->key = malloc(key_len);
230 | p_node->value = malloc(value_len);
231 | if (p_node->key == NULL || p_node->value == NULL) {
232 | // Erro ao alocar memória para a chave ou o valor
233 | free(p_node->key);
234 | free(p_node->value);
235 | free(p_node);
236 | return NULL;
237 | }
238 |
239 | // Copia a chave e o valor para o novo nó
240 | memcpy(p_node->key, key, key_len);
241 | memcpy(p_node->value, value, value_len);
242 | p_node->key_len = key_len;
243 | p_node->value_len = value_len;
244 | p_node->next = NULL;
245 |
246 | // Adiciona o novo nó ao final da lista encadeada no bucket correspondente
247 | if (last_node != NULL) {
248 | last_node->next = p_node;
249 | } else {
250 | hash_table->buckets[index] = p_node;
251 | }
252 |
253 | // Atualiza a contagem total de elementos na tabela
254 | hash_table->count++;
255 | return p_node->value; // Retorna o ponteiro para o valor inserido
256 | }
257 | ```
258 |
259 | vou analisar algumas partes específicas do código da função hash_tab_insert e explicar como funcionam:
260 | ```c
261 | int index = default_hash_func(key, key_len, hash_table->size);
262 | ```
263 | Nesta linha, a função de hash padrão (default_hash_func) é chamada para calcular o índice do bucket onde o novo nó será inserido. O índice é obtido considerando a chave, seu tamanho e o tamanho total da tabela de hash
264 |
265 | ```c
266 | node_t *next_node, *last_node;
267 | next_node = hash_table->buckets[index];
268 | last_node = NULL;
269 | ```
270 | Estas linhas inicializam ponteiros para os nós existentes no bucket (next_node) e para o último nó na lista encadeada (last_node). Isso é crucial para percorrer a lista e verificar se já existe um nó com a mesma chave.
271 |
272 | ```c
273 | while (next_node != NULL) {
274 | if (next_node->key_len == key_len) {
275 | if (memcmp(key, next_node->key, key_len) == 0) {
276 | // Código para atualizar valor existente omitido
277 | }
278 | }
279 | last_node = next_node;
280 | next_node = next_node->next;
281 | }
282 | ```
283 | Este trecho percorre a lista encadeada no bucket para verificar se já existe um nó com a mesma chave. A função memcmp é utilizada para comparar as chaves e determinar se são iguais. Se uma chave igual for encontrada, o código para atualizar o valor existente é executado, mas foi omitido aqui para simplificar a explicação.
284 |
285 | ```c
286 | node_t *p_node = (node_t *)malloc(sizeof(node_t));
287 | if(p_node == NULL) {
288 | return NULL;
289 | }
290 | ```
291 | Essas linhas alocam dinamicamente memória para o novo nó. O tipo de dado genérico é usado para o ponteiro do nó (void*), e malloc é empregado para alocar memória do tamanho adequado.
292 |
293 | ```c
294 | p_node->key = malloc(key_len);
295 | p_node->value = malloc(value_len);
296 | if (p_node->key == NULL || p_node->value == NULL) {
297 | // Código para tratamento de erro omitido
298 | }
299 | ```
300 | Aqui, memória é alocada para as chaves e valores do novo nó. O código trata de forma adequada situações em que a alocação de memória para chave ou valor falha, mas o código específico para tratamento de erro foi omitido por questões de brevidade.
301 |
302 | ```c
303 | memcpy(p_node->key, key, key_len);
304 | memcpy(p_node->value, value, value_len);
305 | p_node->key_len = key_len;
306 | p_node->value_len = value_len;
307 | p_node->next = NULL;
308 | ```
309 | Estas linhas realizam a cópia dos dados da chave e do valor para o novo nó, e os tamanhos são armazenados nos campos key_len e value_len, respectivamente. O campo next é definido como NULL porque o novo nó será o último na lista encadeada.
310 |
311 | ```c
312 | if (last_node != NULL) {
313 | last_node->next = p_node;
314 | } else {
315 | hash_table->buckets[index] = p_node;
316 | }
317 | ```
318 | Este bloco adiciona o novo nó à lista encadeada no bucket apropriado. Se last_node não for NULL, significa que já existem nós na lista, e o novo nó é adicionado ao final. Se last_node for NULL, significa que o bucket estava vazio, e o novo nó é atribuído diretamente ao bucket.
319 |
320 | ```c
321 | hash_table->count++;
322 | return p_node->value;
323 | ```
324 |
325 | Por fim, a contagem total de elementos na tabela é atualizada, e o ponteiro para o valor inserido é retornado, possibilitando a manipulação do valor na aplicação que utiliza a tabela de hash.
326 |
327 | #### Busca
328 | ```c
329 | /**
330 | * Procura um valor associado a uma chave na tabela de hash.
331 | * @param hash_table Ponteiro para a tabela de hash onde realizar a busca.
332 | * @param key Ponteiro para a chave a ser procurada.
333 | * @param key_len Tamanho da chave em bytes.
334 | * @return Ponteiro para o valor associado à chave ou NULL se a chave não for encontrada.
335 | */
336 | void *hash_tab_search(hash_tab_t *hash_table, void *key, size_t key_len) {
337 | // Calcula o índice do bucket usando a função de hash padrão
338 | int index = default_hash_func(key, key_len, hash_table->size);
339 |
340 | // Verifica se o bucket não está vazio
341 | if (hash_table->buckets[index] != NULL) {
342 | // Inicializa o ponteiro para o último nó na lista encadeada
343 | node_t *last_node = hash_table->buckets[index];
344 |
345 | // Percorre a lista encadeada
346 | while (last_node != NULL) {
347 | // Verifica se os tamanhos das chaves são iguais
348 | if (last_node->key_len == key_len) {
349 | // Compara as chaves byte a byte usando memcmp
350 | if (memcmp(key, last_node->key, key_len) == 0) {
351 | // Retorna o valor associado à chave se a chave for encontrada
352 | return last_node->value;
353 | }
354 | }
355 |
356 | // Move para o próximo nó na lista
357 | last_node = last_node->next;
358 | }
359 | }
360 |
361 | // Retorna NULL se a chave não for encontrada
362 | return NULL;
363 | }
364 | ```
365 | A função começa calculando o índice do bucket usando a função de hash padrão. Em seguida, ela verifica se o bucket correspondente não está vazio. Se o bucket estiver vazio, a função retorna NULL, indicando que a chave não foi encontrada na tabela de hash.
366 |
367 | Se o bucket não estiver vazio, a função percorre a lista encadeada associada a esse bucket. Durante esse percurso, ela compara o tamanho da chave (key_len) com o tamanho da chave no nó atual. Se os tamanhos forem iguais, a função usa memcmp para comparar as chaves byte a byte. Se as chaves forem consideradas iguais, o valor associado a essa chave é retornado.
368 |
369 | Se a função percorrer toda a lista encadeada sem encontrar a chave correspondente, ela retorna NULL, indicando que a chave não está presente na tabela de hash.
370 |
371 | Essa função é fundamental para recuperar valores associados a chaves específicas na tabela de hash, tornando-a útil para consultas e operações de busca eficientes.
372 |
373 | #### Remoção
374 | ```c
375 | /**
376 | * Remove um nó associado a uma chave na tabela de hash.
377 | * @param hash_table Ponteiro para a tabela de hash onde realizar a remoção.
378 | * @param key Ponteiro para a chave do nó a ser removido.
379 | * @param key_len Tamanho da chave em bytes.
380 | */
381 | void hash_tab_remove(hash_tab_t *hash_table, void *key, size_t key_len) {
382 | // Inicializa ponteiros para os nós atual e anterior na lista encadeada
383 | node_t *next_node, *last_node;
384 |
385 | // Calcula o índice do bucket usando a função de hash padrão
386 | int index = default_hash_func(key, key_len, hash_table->size);
387 |
388 | // Inicializa next_node com o primeiro nó na lista encadeada do bucket
389 | next_node = hash_table->buckets[index];
390 |
391 | // Inicializa last_node como NULL, pois ainda não há nó anterior
392 | last_node = NULL;
393 |
394 | // Percorre a lista encadeada procurando o nó com a chave
395 | while (next_node != NULL) {
396 | if (next_node->key_len == key_len) {
397 | // Compara as chaves byte a byte usando memcmp
398 | if (memcmp(key, next_node->key, key_len) == 0) {
399 | // Libera a memória alocada para a chave e o valor do nó
400 | free(next_node->value);
401 | free(next_node->key);
402 |
403 | // Atualiza o ponteiro do nó anterior para "pular" o nó a ser removido
404 | if (last_node != NULL) {
405 | last_node->next = next_node->next;
406 | } else {
407 | // Se não há nó anterior, atualiza o ponteiro do bucket
408 | hash_table->buckets[index] = next_node->next;
409 | }
410 |
411 | // Libera a memória alocada para o nó
412 | free(next_node);
413 | break; // Sai do loop, pois o nó foi removido
414 | }
415 | }
416 |
417 | // Move para o próximo nó na lista
418 | last_node = next_node;
419 | next_node = next_node->next;
420 | }
421 | }
422 | ```
423 | A função hash_tab_remove tem como objetivo remover um nó associado a uma chave na tabela de hash. Inicialmente, calcula-se o índice do bucket usando a função de hash padrão, e os ponteiros next_node e last_node são inicializados para percorrer a lista encadeada. O loop de busca compara as chaves byte a byte utilizando memcmp, e se a chave desejada é encontrada, realiza-se a remoção do nó. Durante esse processo, a função libera de forma adequada a memória alocada para a chave, o valor e o próprio nó. Os ponteiros são atualizados para manter a integridade da lista encadeada, seja removendo um nó no meio da lista ou ajustando o ponteiro do bucket. Essa função assegura uma operação eficiente de remoção na tabela de hash, adequando-se dinamicamente às mudanças na estrutura da lista encadeada e garantindo a correta liberação de recursos.
424 |
425 | #### Liberação
426 | ```c
427 | void hash_tab_free(hash_tab_t *hash_table) {
428 | node_t *next_node, *last_node;
429 |
430 | // Libera a memória de cada nó
431 | for (int i = 0; i < (int) hash_table->size; i++) {
432 | next_node = hash_table->buckets[i];
433 | while (next_node != NULL) {
434 | free(next_node->key);
435 | free(next_node->value);
436 | last_node = next_node;
437 | next_node = next_node->next;
438 | free(last_node);
439 | }
440 | }
441 | free(hash_table->buckets);
442 | free(hash_table);
443 | }
444 | ```
445 |
446 | A função hash_tab_free desempenha um papel crucial na liberação de memória associada a uma tabela de hash e seus nós. Utilizando um loop, ela percorre cada bucket da tabela, liberando a memória alocada para as chaves e valores em cada nó da lista encadeada. O processo inclui a liberação de recursos dinâmicos e o ajuste dos ponteiros para garantir uma desalocação adequada. Após a conclusão desse processo para todos os buckets, a função libera a memória alocada para o array de buckets e, por fim, para a própria estrutura da tabela de hash. Essa função é crucial para evitar vazamentos de memória, garantindo a correta desalocação de todos os recursos associados à tabela de hash e seus elementos.
447 |
448 | ### Técnicas utilizadas
449 | De forma resumida neste projeto, foram utilizadas as seguintes técnicas de programação em C:
450 |
451 | - Uso de Ponteiros void para Genéricos*: A utilização de ponteiros do tipo void* permite a criação de estruturas de dados genéricas, permitindo o armazenamento e manipulação de diferentes tipos de dados na tabela de hash.
452 | - Memcpy para Cópia Eficiente de Dados Genéricos: A função memcpy é empregada para realizar cópias eficientes de dados genéricos, sendo crucial para manipular estruturas heterogêneas de maneira flexível. Isso é especialmente útil na função de inserção (hash_tab_insert), onde se copiam chaves e valores para os nós da tabela.
453 | - Memcmp para Comparação de Chaves Genéricas: A função memcmp é utilizada para comparar chaves genéricas, permitindo a busca eficiente na tabela de hash. Essa técnica é aplicada na função de busca (hash_tab_search), possibilitando a identificação de chaves iguais, independentemente do tipo de dado.
454 | - Alocação Dinâmica e Liberação de Memória: A alocação dinâmica de memória é essencial para lidar com estruturas de dados genéricas. Utilizando malloc e free, é possível alocar e liberar memória de forma dinâmica para chaves, valores e nós na tabela de hash.
455 | - Casting Adequado para Manutenção da Genericidade: O uso de castings apropriados, como (void*) e (int ()(void, size_t, size_t)), é essencial para garantir a compatibilidade e manutenção da genericidade ao longo do código.
456 |
457 | ### Referências:
458 | [hscHeric - hsc_hash_separate-chaining](https://github.com/hscHeric/hsc_hash_separate-chaining)
459 |
460 | Kernighan, B. W., & Ritchie, D. M. (1988). The C Programming Language (2nd ed.). Prentice Hall.
461 |
462 | Robbins, A., & Robbins, A. (2004). Unix systems programming: Communication, concurrency and threads (Vol. 2). Prentice Hall PTR.
463 |
464 | [Manpage oficial da função memcpy](https://www.man7.org/linux/man-pages/man3/memcpy.3.html)
465 |
466 | [Manpage oficial da função memcmp](https://www.man7.org/linux/man-pages/man3/memcmp.3.html)
467 |
468 |
--------------------------------------------------------------------------------
/.github/id_4noobs_slogan.svg:
--------------------------------------------------------------------------------
1 |
2 |
265 |
--------------------------------------------------------------------------------