#+title: Curso Básico da Linguagem C
#+subtitle: Aula 4: Variáveis e ponteiros
#+author: Blau Araujo
#+startup: show2levels
#+options: toc:3

* Variáveis e ponteiros

- *Variáveis* são elementos da linguagem que representam dados na memória.
- *Ponteiros* são variáveis que representam endereços de dados na memória.
  
** Declaração e definição

Variáveis precisam ser declaradas e definidas antes de serem utilizadas.

Exemplo (=var1.c=):

#+begin_src c
#include <stdio.h>

int main(void) {

    int a = 10;  /* Variável declarada e inicializada (definida). */
    int b;       /* Variável declarada, mas não inicializada! */ 

    printf("a = %d\n", a);
    printf("b = %d\n", b);
    printf("a + b = %d\n", a + b);

    return 0;
}
#+end_src

Compilando e executando:

#+begin_example
:~$ gcc -o var1 var1.c
:~$ ./var1
a = 10
b = 361680176
a + b = 361680186
#+end_example

Observe:

- A compilação não emitiu avisos e nem terminou com erro.
- A variável =b= (não inicializada) avaliou um valor qualquer.

*** Por que isso aconteceu?

Ao ser declarada, a variável recebeu um endereço e um tipo (=int=). Como não
foi inicializada, seu valor será o que quer que esteja nos 4 bytes da
memória a partir do endereço designado para =b=.

Por exemplo:

#+begin_example
a -> 0x7ffd45c33fdc: 0a 00 00 00 (10)
b -> 0x7ffd45c33fd8: 3a cd 8e 15 (361680176) <-- LIXO!
#+end_example

*** Como evitar esse erro?

A primeira (e mais óbvia) opção é não se esquecer de atribuir um valor
à variável antes de usá-la, mas a compilação poderia ter sido feita com
as opções de avisos:

- =-Wall=: Habilita todos os avisos sobre problemas evitáveis no código.
- =-Wextra=: Habilita avisos adicionais.
- =-Werror=: Trata todos os avisos como erros fatais.

Exemplo:

#+begin_example
:~$ gcc -o var1 var1.c -Wall -Wextra -Werror
var1.c: In function ‘main’:
var1.c:10:5: error: ‘b’ is used uninitialized [-Werror=uninitialized]
   10 |     printf("b = %d\n", b);
      |     ^~~~~~~~~~~~~~~~~~~~~
var1.c:7:9: note: ‘b’ was declared here
    7 |     int b;       /* Variável declarada, mas não inicializada! */
      |         ^
cc1: all warnings being treated as errors
#+end_example

** Atributos das variáveis

Toda variável tem como atributos...

- Um nome (identificador);
- Um valor associado;
- O endereço do valor associado.

O nome da variável expressa o seu valor:

#+begin_src c
int a = 21;
int b = a;       // O valor de 'b' é o valor de 'a' (21).
int c = a + b;   // O valor de 'c' é 21+21 (42).
#+end_src

Para que uma variável expresse o endereço do valor associado, nós utilizamos
o operador unário =&= antes de seu nome:

#+begin_src c
int a = 123;

printf("Valor de a   : %d", a);
printf("Endereço de a: %p", &a); // Imprime o endereço do dado em 'a'.
#+end_src

O tamanho do tipo da variável é expresso com o operador =sizeof(VAR)=:

#+begin_src c
int a = 10;
int b = sizeof(a);  // O valor de 'b' será 4 (bytes).
#+end_src

#+begin_quote
O tipo do valor expresso por =sizeof= é =size_t=, um apelido para =unsigned long=.
#+end_quote

** Escopo de variáveis

- Toda variável declarada numa função só é visível na própria função (escopo /local/).
- Toda variável declarada fora de funções é visível em todo o programa (escopo global).

Exemplo (=var3.c=):

#+begin_src c
#include <stdio.h>

int c = 17;    // Variável global.

// As variáveis 'num' e 'b' são locais à função 'soma10'.
int soma10(int num) {
    int b = 10;
    return b + num;
}

int main(void) {

    int a = 25;    // A variável 'a' é local à função 'main'.

    // Isso causará um erro na compilação!
    printf("a = %d\nb = %d\nc = %d\nnum = %d\n", a, b, c, num);

    return 0;
}
#+end_src

** Ponteiros

Antes de qualquer coisa, os ponteiros são simplesmente variáveis, embora
possuam algumas particularidades:

- São declarados com o operador de derreferência (ou indireção) =*NOME=.
- Recebem endereços como valores.
- Seus tipos são suas unidades aritméticas.
- Seus nomes expressam os endereços associados.
- Quando derreferenciados (=*NOME=), expressam os valores nos endereços.

*** Declaração do ponteiro

#+begin_src c
int a = 25;
int *pa = &a;  // O valor de 'pa' será o endereço de 'a'.
int b = *pa;   // O valor de 'b' será o valor no endereço em 'pa'.
#+end_src

** Perguntas da aula

*** A cada execução do programa, os endereços das variáveis mudam?

Sim, porque o /espaço de endereços/ é designado pelo sistema operacional
assim que o processo para executar o programa é criado.

*** O ponto da declaração de uma variável global faz diferença?

Sim, toda variável tem que ser declarada antes do ponto no código
em que é utilizada.
