Variáveis, tipos de dados e modificadores de tipo

            Um dos conceitos mais primordiais quando falamos de programação é o de variável. Primeiro precisamos saber em que local as informações de um processo (programa em execução) são armazenadas nos computadores: na memória RAM. A memória RAM é uma peça de hardware volátil que tem como função básica armazenar as instruções de um processo e servir como uma memória intermediária auxiliando o processador no processamento das instruções. As informações respectivas ao programa armazenadas no periférico de armazenamento são movidas para a RAM ao abri-lo o e através de um ciclo de busca e execução o processador realiza todo o processamento. Toda a memória RAM é segmentada em diversos segmentos de memória divididos em endereços de memória cujos tamanhos são medidos em bytes. São nesses "locais" que as informações de absolutamente tudo a respeito dos programas são armazenadas — nos endereços de memória. Na prática, uma variável é uma abstração para um endereço de memória, ou seja, uma variável vai armazenar alguma informação enquanto um programa estiver em execução. Abaixo, as duas imagens ilustram um processo carregado em memória:

Disco, memória e processador Disco, memória e processador

            Na primeira imagem, vemos um programa armazenado em um disco rígido sendo carregado na memória RAM. Os computadores contam com um MMU (Memory Management Unit) implementado como parte do processador ou como um circuito integrado separado cuja função é converter os endereços de memória físicos da RAM para endereços de memória virtuais com o auxílio de uma tabela chamada tabela de paginação. As instruções de um processo são armazenados propriamente nesses endereços virtuais, para então serem processados pelo processador.

            Na segunda imagem, observamos a ilustração de uma memória RAM e uma pequena região de memória onde alguns dados estão armazenados. Vamos tomar como exemplo o endereço de memória 0x10d3ba0c8. Podemos dizer, a grosso modo, que esse endereço seria a representação "física" (não tão física assim) de uma variável que, no que lhe concerne, será utilizada para armazenar alguma informação importante para o programa.

            O tamanho em bytes de um endereço de memória é definido conforme a arquitetura em uso ou como o programa foi compilado: em arquiteturas de 32 bits um endereço de memória possui 4 bytes e em arquiteturas de 64 bits um endereço de memória possui 8 bytes. Uma variável pode ocupar mais de um endereço de memória, dependendo de seu tamanho pode ocupar inúmeros endereços, como é o caso dos arrays.

            Agora que sabemos como as informações são armazenadas em memória, iremos falar mais precisamente sobre quais tipos de dados são armazenados. O termo tipo de dado é bem autoexplicativo e se refere plenamente a isso: qual categoria de dado irá ocupar um ou vários endereços de memória. Basicamente, temos duas categorias de dados (três se considerarmos o tipo booleano): numéricos e caracteres. Os númericos são divididos entre inteiros e reais (que possuem casas decimais). Os caracteres podem ser únicos ou vários formando uma string. A linguagem C divide essas duas categorias em cinco tipos de dados para usarmos em nosso códigos, segue abaixo quais são:

            Cada tipo de dado possui um tamanho em bytes que irá ocupar espaço na memória. Além de ocupar espaço, o tamanho implica no intervalo de valores que aquele tipo pode representar.

            Sabemos o que são variáveis e quais tipos de dados a linguagem C nos fornece, então vamos ver como se define uma variável em um código-fonte. Para definir uma variável, a sintaxe segue a forma tipo_de_dado identificador = valor. Abaixo, há um exemplo de definição de uma variável do tipo int:

1    #include <stdio.h>
2
3    int main(void) {
4
5          int x = 10;
6          printf("x = %d\n", x);
7
8          return 0;
9
10    }

            O int que antecede o identificador especifica que estamos criando uma variável inteira, x define o identificador da variável, = indica uma atribuição e 10 é o valor inteiro atribuído a x. Acrescentamos ao printf() mais alguns detalhes para imprimir o valor de x. Tudo o que estiver entre aspas "" representa o que será escrito na tela, nesse caso, x = 10. %d é um especificador de formato da linguagem C e serve como um marcador de posição onde o valor de uma variável inteira ou um valor literal inteiro deve aparecer na string'. \n é um dos vários caracteres de escape da linguagem C, que funcionam como caracteres de controle, mas não são caracteres gráficos, nesse caso, \n é uma quebra de linha, para imprimir a mensagem e saltar o cursor para a próxima linha. Por último, fora das aspas e separado por vírgula está o identificador da variável (x), indicando que o especificador %d deve ser substituído por seu valor. Ao executar o programa, a saída que obtemos é a seguinte:

1ª imagem

Obs.: O especificador %i também pode ser utilizado para imprimir números inteiros.

            Definindo uma variável do tipo char (um caractere que suporta a codificação ASCII):

1    #include <stdio.h>
2
3    int main(void) {
4
5          char x = 'a';
6          printf("x = %c\n", x);
7
8          return 0;
9
10    }

            Ao criarmos um variável que armazena um caractere, basta mudarmos a indicação do tipo de dado para char. A atribuição para um char também muda, um caractere deve ser escrito entre aspas simples ''. O especificador do char é um %c e o identificador da variável deve correspondê-lo. Ao executar o programa, a saída que obtemos é a seguinte:

2ª imagem

            Definindo uma variável do tipo float (real de precisão simples):

1    #include <stdio.h>
2
3    int main(void) {
4
5          float x = 10.5;
6          printf("x = %f\n", x);
7
8          return 0;
9
10    }

            Usamos float para criarmos uma variável real de precisão simples (suporta com exatidão até 7 casas decimais). Na atribuição, um número inteiro ou com casas decimais deve ser fornecido (um ponto . deve separar a parte inteira da parte fracionária). Usamos um %f como especificador para uma variável do tipo float. Ao executar o programa, a saída que obtemos é a seguinte:

3ª imagem

            Definindo uma variável do tipo double (real de dupla precisão):

1    #include <stdio.h>
2
3    int main(void) {
4
5          double x = 10.5;
6          printf("x = %lf\n", x);
7
8          return 0;
9
10    }

            Não há muita diferença na definição de uma variável float para uma variável double (suporta com exatidão até 15 casas decimais). Especificamos o tipo de dado como double, mas a atribuição de um valor real continua igual. O especificador de formato para double é o %lf, mas %f ainda pode ser usado. Há uma diferença na leitura de dados reais, mas veremos isso mais adiante. Ao executar o programa, a saída que obtemos é a seguinte:

4ª imagem

            Por padrão, um número real é impresso com 6 casas decimais, mas podemos especificar quantas casas queremos com a seguinte sintaxe: %.nf, onde n representa o número de casas decimais. Eis um exemplo imprimindo um real com somente duas casas decimais:

1    #include <stdio.h>
2
3    int main(void) {
4
5          float x = 10.5;
6          printf("x = %.2f\n", x);
7
8          return 0;
9
10    }

            Ao executar o programa, a saída que obtemos é a seguinte:

5ª imagem

            Os tipos de dados apresentados e seus respectivos especificadores de formato são os principais da linguagem C. Ainda temos o tipo void, mas esse tipo não pode ser usado com variáveis, somente com funções e ponteiros. Todos os tipos de dados vistos até aqui são conhecidos como tipos primitivos ou básicos.

            Podemos imprimir inúmeras variáveis de vários tipos de dados diferentes, utilizando variáveis ou valores literais. Abaixo, um exemplo de printf() imprimindo todos os tipos básicos de dados:

1    #include <stdio.h>
2
3    int main(void) {
4
5          int x = 10, y = 5;
6          char c = 'b';
7          float f = 12.25;
8          double d = 19;
9
10        printf("\nx = %d\ny = %d\nc = %c\nf = %f\n%f\nd = %.4lf\n", x, y, c, f, 20.5, d);
11
12        return 0;
13
14    }

            Na linha 3, foram definidas duas variáveis em uma só linha, isso é possível separando cada identificador por vírgula e atribuindo valor para cada um. No printf() da linha 8, foram impressas as cinco variáveis definidas e o valor literal 20.5. A variável d foi inicializada com um valor inteiro, mas impresso com quatro casas decimais. Ao executar o programa, a saída que obtemos é a seguinte:

6ª imagem

            Ao nome que damos a uma variável, o computador associa o endereço do espaço que foi reservado na memória para guardá-la. De modo geral, interessa ao programador saber o nome das variáveis. Porém, existem algumas regras para a escolha dos nomes das variáveis na linguagem C:

            A tabela a seguir apresenta alguns nomes possíveis de variáveis e outros que fogem às regras estabelecidas:

Nomeando variáveis
comp! .var int! 1var 1cont -x Va-123
X
cont Cont Val_123 _teste Int1 Cont1 X
Check

Obs.: Palavras-chave não podem ser usadas como nome de variáveis..

            Uma variável, como o próprio nome indica, pode variar seu valor. Portanto, podemos atribuir um determinado valor para uma variável e posteriormente no código alterarmos esse valor (não o tipo de dado, somente o valor). Abaixo, um exemplo alterando o valor de uma variável:

1    #include <stdio.h>
2
3    int main(void) {
4
5          int x = 10;
6          printf("x = %d\n", x);
7
8          x = 20;
9          printf("x = %d\n", x);
10
11        return 0;
12
13    }

            No código acima, a variável x foi definida com o valor 10 na linha 3 e foi redefinida para 20 na linha 6. Ao executar o programa, a saída que obtemos é a seguinte:

7ª imagem

            Além das variáveis, existem as constantes, onde o valor não varia. Uma constante permite guardar determinado dado ou valor na memória do computador, mas com a certeza de que esse valor não irá se alterar durante a execução do programa. Para definirmos uma constante, utilizaremos a palavra-chave const. Abaixo, um exemplo definindo uma constante:

1    #include <stdio.h>
2
3    int main(void) {
4
5          const int x = 10;
6          printf("x = %d\n", x);
7
8          return 0;
9
10    }

            Ao executar o programa, a saída que obtemos é a seguinte:

8ª imagem

            Se tentássemos redefinir o valor de uma constante, haveria um erro de compilação:

1    #include <stdio.h>
2
3    int main(void) {
4
5          const int x = 10;
6          printf("x = %d\n", x);
7
8          x = 20;
9          printf("x = %d\n", x);
10
11        return 0;
12
13    }

            Ao executar o programa, a saída que obtemos é a seguinte:

9ª imagem

            A mensagem de erro acima nos informa que tentamos trocar o valor de uma constante (apenas leitura).

Obs.: Para constantes, é obrigatória a atribuição do valor no momento da definição.

            E se não inicializarmos nossa variável? Qual valor será assumido? Bom, se não atribuirmos um valor para uma variável, a mesma será inicializada com o que chamamos de lixo de memória. Um lixo de memória é um valor arbitrário que se encontrava naquele espaço de memória antes da sua reserva para a nossa variável. Esse valor, que pode ser qualquer um, continua lá após o compilador reservar o espaço de memória, portanto, se a variável não for inicializada, esse valor indefinido será o seu conteúdo. Abaixo, um exemplo imprimindo o valor de uma variável não inicializada:

1    #include <stdio.h>
2
3    int main(void) {
4
5          int x;
6          printf("x = %d\n", x);
7
8          return 0;
9
10    }

            Ao executar o programa, a saída que obtemos é a seguinte:

10ª imagem

            Perceba que um valor aleatório foi impresso, nesse caso, o valor 2203648. Como não atribuímos um valor para a variável x, 2203648 se tornou seu conteúdo.

            O tipo de dado char permite que usemos um único caractere, mas também podemos ter um "conjunto" de char para formarmos uma string. Ainda usaremos o tipo char, porém vamos precisar definir um array unidimensional (também chamado de vetor) para criarmos uma cadeia de caracteres. Para definir um array de char, a sintaxe deve ser char identificador[tamanho do array]. Usamos colchetes [] para criar um array e definimos o tamanho (um número inteiro) para o mesmo. Exemplo:

char array[5];

            Acima, criamos um array de cinco posições. Cada char tem um byte, então o tamanho do nosso array é de 5 bytes. Assim como as variáveis, podemos inicializar nosso array com alguma informação:

char array[10] = "String";

            Além de criarmos nosso array, o inicializamos com a string String entre aspas duplas "". Agora, o tamanho do array foi definido como 10 bytes, porém apenas seis posições estão sendo ocupadas. No programa abaixo, definimos uma string e imprimimos seu conteúdo:

1    #include <stdio.h>
2
3    int main(void) {
4
5          char array[10] = "String";
6          printf("array = %s\n", array);
7
8          return 0;
9
10    }

            Para imprimir uma string em linguagem C, usamos o especificador de tipo %s.

            Ao executar o programa, a saída que obtemos é a seguinte:

11ª imagem

            Além dos cinco tipos básicos, a linguagem C possui quatro modificadores de tipos. Esse modificadores são aplicados precedendo os tipos básicos (com exceção do tipo void), e permitem alterar o significado do tipo, de modo a adequá-lo às necessidades do programa. São eles:

            O modificador signed determina que uma variável declarada dos tipos char e int poderá ter valores positivos ou negativos. Trata-se do modo padrão de definição de variáveis desses tipos e, por esse motivo, raramente é usado. Exemplo:

signed char x;
signed int y;

            O modificador unsigned determina que uma variável declarada dos tipos char e int poderá ter valores positivos e o valor zero. Nesse caso, a variável perde seu bit de sinal, o que dobra a sua capacidade de armazenamento para valores positivos. Por exemplo, uma variável do tipo char é capaz de armazenar valores de -128 até 127. Se a mesma variável for declarada como sendo do tipo unsigned char, será capaz de armazenar valores de 0 até 255. A seguir, dois exemplos de uso:

unsigned char x;
unsigned int y;

            O modificador short determina que uma variável do tipo int terá apenas 16 bits (inteiro pequeno), independentemente do processador. Exemplo:

short int i;

            O modificador long faz o inverso do modificador short, ou seja, determina que uma variável do tipo int terá 32 bits (inteiro grande), independentemente do processador. Também determina que o tipo double possua maior precisão. Exemplo:

long int n;
long double d;

            A linguagem c permite que se use mais de um modificador de tipo sobre um mesmo tipo. Isso permite, por exemplo, declarar um inteiro grande (ou seja, com 32 bits) usando o modificador long e que também seja sem sinal com unsigned. Essa combinação permite aumentar em muito o intervalo de valores possíveis para aquela variável:

unsigned long int m;

            A tabela a seguir mostra todas as combinações permitidas dos tipos básicos e dos modificadores de tipo, o seu tamanho em bits e o seu intervalo de valores:

Tipos e combinações de modificadores possíveis
Tipo Bits Intervalo de valores
char 8 -128 a 127
unsigned char 8 0 a 255
signed char 8 -128 a 127
int 32 -2.147.483.648 a 2.147.483.647
unsigned int 32 0 a 4.294.967.295
signed int 32 -2.147.483.648 a 2.147.483.647
short int 16 -32.768 a 32.767
unsigned short int 16 0 a 65.535
signed short int 16 -32.768 a 32.767
long int 32 -2.147.483.648 a 2.147.483.647
unsigned long int 32 0 a 4.294.967.295
signed long int 32 -2.147.483.648 a 2.147.483.647
float 32 1,175494E-038 a 3,402823E+038
double 64 2,225074E-308 a 1,797693E+308
long double 96 3,4E-4932 a 3,4E+4932

Página anterior

Próxima página