<aside>

Conhecimentos necessários:

Variáveis, entrada e saída de dados, estruturas condicionais e estruturas repetitivas

Dados, bases numéricas e memória

Sub-rotinas (funções)

Estruturas de dados homogêneas: array (vetor/matriz)

Introdução a ponteiros

</aside>

<aside>

Navegação

</aside>

Agora, que tal aprender sobre os dados complexos?

Struct

Struct é basicamente um bloco de várias variáveis de uma só vez. Com ele, podemos manter nosso código mais eficiente e organizado. Para definir uma struct, basta escrever isso fora e acima das funções:

struct NomeDaStruct {variáveis};

Após isso, declarar uma variável desse tipo:

struct NomeDaStruct NomeDaVariavel;

Aí, para acessar os elementos dentro dela, basta usar um ponto. Um exemplo:

#include <string.h>

struct Aluno {
	char nome[100];
	int idade;
	char turma[5];
	char nota;
};

int main() {
	struct Aluno joao;
	
	strcpy(joao.nome, "Joao");
	joao.idade = 14;
	strcpy(joao.turma, "8º D");
	joao.nota = 'B';
	
	return 0;
}

Ou, também, podemos colocar tudo de uma vez, parecido com vetores:

struct Aluno {
	char nome[100];
	int idade;
	char turma[5];
	char nota;
};

int main() {
	struct Aluno joao = {"Joao", 14, "8º D", 'B'};
	
	return 0;
}

Um ponto importante é: Como essa struct possui 4 membros dentro dela (4 tipos de variáveis), ela tem o mesmo peso, em bytes, dessas 4 variáveis juntas. No caso:

100 bytes (do char nome[100]) + 4 bytes (do int idade) + 5 bytes (do char turma[5]) + 1 byte (do char nota) = 110 bytes;

Toda struct possui o peso de todos os elementos somados.

Vetores e aninhamento

Caso queira, pode criar vetores de structs ou usar structs dentro de structs (que é o que chamamos de aninhamento de structs). Por exemplo:

struct Pessoal {
	char nome[100];
	int idade;
	char cpf[15]; //CPF sempre é em string. Nunca pode ser em int por causa que existem CPFs que começam com zeros
};

struct Data {
	int dia;
	int mes;
	int ano;
};

struct Aluno {
	struct Pessoal infoPessoal;
	struct Data dataNascimento;
	char turma[5];
	int notas[3];
};

int main() {
	struct Aluno alunos[10];
	
	//primeiro aluno
	strcpy(alunos[0].infoPessoal.nome, "Joaquim Pereira");
	alunos[0].infoPessoal.idade = 17;
	strcpy(alunos[0].infoPessoal.cpf, "123.456.789-09");
	alunos[0].dataNascimento.dia = 1;
	alunos[0].dataNascimento.mes = 1;
	alunos[0].dataNascimento.ano = 2008;
	strcpy(alunos[0].turma, "2ºEM");
	alunos[0].notas[0] = 10;
	alunos[0].notas[1] = 8;
	alunos[0].notas[2] = 9;
	
	//depois só fazer o mesmo para os outros alunos, ou dar a possibilidade para o cliente escrever as informações
	return 0;
}

Retorno de funções

Também é possível criarmos funções com retornos das structs que nós mesmos criamos. Tudo funciona de maneira análoga. Um exemplo de uso:

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

struct Raizes {
	float x1;
	float x2;
};

struct Raizes FormulaQuadratica(float a, float b, float c){
	float delta = b*b - 4*a*c;
	float x1 = (-b + sqrt(delta))/(2*a);
	float x2 = (-b - sqrt(delta))/(2*a);
    struct Raizes solucoes = {x1, x2};
	return solucoes;
}

int main() {
	struct Raizes funcao = FormulaQuadratica(1, -5, 6); //raízes da função f(x) = x² -5x + 6
	printf("As raizes sao: %f e %f.", funcao.x1, funcao.x2); //imprime "As raizes sao: 3.000000 e 2.000000."
	return 0;
}

Ponteiros

Também é possível fazer ponteiros para structs e usar como usamos um ponteiro normalmente. Por exemplo:

struct Aluno {
	char nome[100];
	int idade;
	char turma[5];
	char nota;
};

int main() {
	struct Aluno joao;
	
	struct Aluno *ponteiro = &joao;
	(*ponteiro).idade = 27; //faz o papel de joao.idade = 27;
	
	return 0;
}

Mas evitar ficar escrevendo assim, podemos também escrever usando ->. Tipo assim:

struct Aluno {
	char nome[100];
	int idade;
	char turma[5];
	char nota;
};

int main() {
	struct Aluno joao;
	
	struct Aluno *ponteiro = &joao;
	ponteiro->idade = 27; // mesma coisa de (*ponteiro).idade = 27;
	ponteiro->nota = 'C'; // mesma coisa de (*ponteiro).nota = 'C';
	
	return 0;
}

Union

Union é parecida com uma struct, mas no lugar de poder armazenar vários tipos de dados ao mesmo tempo, ela só pode armazenar um dado de cada vez. Por exemplo:

#include <stdio.h>

union Teste {
    int inteiro;
    char caractere;
    float flutuante;
};

int main() {
	union Teste var;
    var.inteiro = 10; //só está armazenando o número 10
    var.caractere = 'A'; //só está armazenando o caractere 'A'. O número 10 foi apagado
    var.flutuante = 3.14; //só está armazenando o número decimal 3.14. O caractere 'A' foi apagado

    printf("%f", var.flutuante); //mostrará "3.140000"
	return 0;
}

Ela só armazena um único dado por vez por dividir o mesmo espaço na memória para todas as variáveis, assim, o peso dela, em bytes, é da maior variável dentro da union. Como nesse caso criamos uma union com um int, char e float dentro, ela pesará 4 bytes, pois a variável mais pesada dentro dela (int ou float, nesse caso) possui esse peso.

Enum

Uma enum é como se fosse um bloco de constantes. Dentro dela, escrevemos alguns nomes que funcionará como números (ou seja, basicamente uma constante mesmo). Um exemplo de uso:

enum DiaDaSemana {
    DOMINGO,
    SEGUNDA,
    TERCA,
    QUARTA,
    QUINTA,
    SEXTA,
    SABADO
};

int main() {
	enum DiaDaSemana dia1 = DOMINGO;
  enum DiaDaSemana dia2 = SEGUNDA;

	return 0;
}

É realmente como se estivéssemos criando constantes. Podemos também fazer uma atribuição em números para caso precisamos ter um melhor controle do valor de cada opção. Tipo assim:

enum DiaDaSemana {
    DOMINGO = 10,
    SEGUNDA = 20,
    TERCA = 30,
    QUARTA = 40,
    QUINTA = 50,
    SEXTA = 60,
    SABADO = 70
};

int main() {
	enum DiaDaSemana dia1 = DOMINGO;
  enum DiaDaSemana dia2 = SEGUNDA;

	return 0;
}

Caso não façamos essa atribuição, os valores padrões seguem uma contagem começando do zero.

Typedef

É uma forma de apelidarmos essas estruturas para podermos criá-las mais facilmente depois. Para isso, basta escrever typedef antes de declarar uma estrutura e depois de fechar as chaves - mas antes do ponto e vírgula, escrever o apelido que deseja atribuir. Um exemplo:

#include <stdio.h>

typedef enum DiaDaSemana {
    DOMINGO = 10,
    SEGUNDA = 20,
    TERCA = 30,
    QUARTA = 40,
    QUINTA = 50,
    SEXTA = 60,
    SABADO = 70
} DiaDaSemana;

typedef union Dado {
    int i;
    char c;
    float f;
} Dado;

typedef struct Player {
    int id;
    int hp;
    int pts;
} Player;

int main() {
	Player jogador1, jogador2;
    Dado vida_jogador1;
    vida_jogador1.i = 100;
    Dado vida_jogador2;
    vida_jogador2 = vida_jogador1; //atribuindo um tipo Dado a outro tipo Dado. Como um guarda o int, automaticamente o outro guarda um int também 
    DiaDaSemana dia_atual = SEGUNDA;
    
    printf("%d", vida_jogador2.i); //mostrará "100"

	return 0;
}