Variáveis

Declaração e inicialização

A maneira mais simples de armazenar e acessar dados em C é pelo uso de objetos, que são regiões na memória que podem representar valores de diversos tipos. Uma forma simples de criar um objeto consiste em nomeá-lo e especificar seu tipo, com a seguinte sintaxe:

tipo nome

Assim, após a declaração int x;, x irá se referir a um objeto do tipo int.

Como a variável x foi definida sem algum valor especificado, na maioria dos casos ela é uma variável não inicializada, ou seja, armazena um valor "lixo" que já estava na memória. Para inicializá-la, basta especificar um inicializador (valor inicial), utilizando a sintaxe:

tipo nome = expr1

1

Expressão que produz um valor.

Substituindo int x; por int x = 5;, x passa a possuir o valor inicial especificado.

Uso

O valor de uma variável pode ser acessado e modificado durante a execução do programa.

Aqui está um código e um diagrama que representa as alterações no valor x durante sua execução:

int main(void)
{
    int x = 5;

    x = x + 5;
    x = x - 9;
    x = x + x + x;

    return 0;
stateDiagram
    Direction LR
    5-->10: x = x + 5
    10-->1: x = x - 9
    1-->3: x = x + x + x

O identificador de uma variável é uma expressão que produz o valor armazenado em seu respectivo objeto, portanto após a expressão x = 10, a expressão x produz o valor 10; nesse caso x + 3 é o mesmo que 10 + 3.

Escopo

Todos os identificadores, como nomes de variáveis e funções, possuem um escopo que determina onde podem ser acessados.

Escopo de Bloco

Os parâmetros de uma função podem ser acessados apenas em seu corpo, isso significa que n pode ser acessado em foo mas não em bar:

int foo(int n)
{
    return n; // Okay
}

int bar(void)
{
    return n; // Erro: n não existe nesse contexto
}

Isso se chama escopo de bloco, ou seja, o identificador é acessível dentro do bloco ({}) envolvente. No caso da seguinte variável n, seu escopo inicia em sua declaração e termina no final do bloco.

int foo(void)
{
    int n;

    return n; // Okay
}

int bar(void)
{
    return n; // Erro: n não existe nesse contexto
}

Um identificador também não pode ser definido duas vezes no mesmo bloco, mas blocos podem ser aninhados:

int foo(void)
{
    int n;

    int n; // Erro: n já foi definido nesse bloco
}

int bar(void)
{
    int n;

    {
        int n; // Okay: Este n está contido apenas nesse bloco
    }
}

Vale lembrar que mesmo sendo o mesmo identificador, n representa uma entidade diferente em cada escopo em que é declarado:

int foo(void)
{
    int n = 5;

    {
        int n = 10;

        return n; // Isso retorna 10 e não 5, pois a redeclaração de n torna o n
                  // anterior inacessível
    }

    return n; // Isso retorna 5 pois o escopo do segundo n termina e o primeiro
              // volta a estar acessível
}

Caso um identificador não seja redeclarado em um bloco aninhado, sua declaração original será acessada:

int foo(void)
{
    int n = 5;

    {
        return n; // Isso retorna 5
    }
}

Escopo de Arquivo

Uma variável declarada fora de um bloco possui escopo de arquivo—pode ser acessada em qualquer lugar do arquivo após sua declaração.

int n = 5;

int foo(void)
{
    return n; // Retorna 5
}

int bar(void)
{
    return n; // Retorna 5
}

Diferente de variáveis com escopo de bloco, variáveis com escopo de arquivo são inicializadas com valores definidos de acordo com seus tipos. Se o inicializador fosse removido do código acima n armazenaria 0, enquanto se n tivesse escopo de bloco não haveria nenhuma garantia de seu valor.

A sequência //, desde o padrão C99, transforma o resto de uma linha em um comentário—trecho que será ignorado.

Trechos iniciados em /* e terminados em */ são comentários que podem abranger múltiplas linhas.