Curso de C++/Tipos de dados
De Wikiversidade
Tabela de conteúdo |
[editar] Tipos primitivos e compostos
C++ tem 6 tipos de dados primitivos, a saber: char, int, float, double, wchar_t e bool. São, respectivamente, os tipos: caracter ASCII, inteiro, ponto flutuante de precisão simples, ponto flutuante de dupla precisão, caracter UTF-16 e lógico.
A partir desses tipos de dados, podemos formar tipos de dados compostos (veremos alguns aqui). O tipo char, por exemplo, pode ser utilizado para criar strings de caracteres. Os tipos compostos de dados que aprenderemos nesta seção são: vetores, matrizes, strings e structs.
[editar] Tipos primitivos
[editar] Caracter ASCII
char umCaracter = 'a';
O tipo de dados caracter é utilizado para armazenar letras, números e outros caracteres especiais. A declaração do tipo dá-se através da palavra reservada char.
Ao total, são 256 caracteres que podem ser armazenados, dos quais os 128 primeiros fazem parte do conjunto ASC II. Você pode declarar um caracter de duas formas: com ou sem aspas. Ao declarar um caracter sem as aspas simples, você deverá saber qual é o decimal correspondente. Por exemplo, se você digitar char caracter = 97, o console imprimirá a letra 'a'. Logicamente, você poderia ter declarado esta variavel com char caracter = 'a'.
Você também pode armazenar números inteiros do intervalo de -128 até 127. Você poderá realizar operações matemáticas entre esses números, se quiser. Mas tenha em mente que se você for imprimir na tela o valor, o resultado será o caracter correspondente na tabela. Por exemplo:
char caracter1 = 50; // corresponde ao caracter '2'. char caracter2 = 47; // corresponde ao caracter '/'. caracter1 = caracter1 + caracter2; // caracter1 agora tem o valor 97, que corresponde a letra 'a'. cout << "Imprimiu o caracter: " << caracter1 << endl; // Imprimirá a letra 'a'. // Se você precisar imprimir o número, faça o seguinte: int inteiro = caracter1; // Armazena o valor inteiro que está em char. cout << "Imprimiu: " << inteiro << endl; // Imprime o número. // Você pode fazer isto também: cout << "Imprimiu: " << (int) caracter1 << endl;
A saída deste trecho de código no console se parecerá com:
Imprimiu o caracter: a Imprimiu: 97 Imprimiu: 97
Se um dia precisar descobrir o código do caracter, você pode fazer o seguinte:
char caracter = 'B'; // declare entre aspas simples o caracter a ser descoberto. cout << "O código do caracter é: " << (int) caracter << endl; // converte para inteiro antes de imprimir.
A saída na tela se parecerá com:
O código do caracter é: 66
Você poderá somar caracteres. Por exemplo: somar '2' com '/' equivale a letra 'a', pois 50 + 47 = 97.
[editar] Caracter UTF-16
Existe o tipo wchar_t, que também é uma palavra reservada da linguagem C++. A declaração e o uso seguem as mesmas regras do tipo caracter, porém, o conjunto de caracteres que esse tipo suporta é muito maior (65.536 caracteres ao total). O conjunto de caracteres desse tipo é o Unicode codificado em UTF-16. Assim sendo, caracteres especiais como letras com acentuação, cedilha, e outros podem ser lidos e impressos.
Importante: Se você tentar imprimir um wchar_t na janela de console, você talvez não conseguirá (poderá aparecer um número de 0 até 65.535). Isso dependerá do conjunto de caracteres suportados pelo console. Entretanto, se você estiver realizando a leitura de dados digitados pelo usuário, esse tipo é mais adequado que char, pois o usuário poderá digitar alguns caracteres do teclado não suportados pelo tipo char.
wchar_t caracterext = 'ç'; // o caracter de cedilha faz parte do conjunto de caracteres Unicode em codificação UTF-16.
[editar] Inteiros
O tipo inteiro é um tipo bastante comum. Sua declaração dá-se através da palavra reservada int.
int inteiro = 500;
O tipo inteiro pode armazenar números que vão da faixa de -2.147.483.648 até 2.147.483.647 (um total de 232 números, ou 4.294.967.296). Você pode modificar esta faixa de valores usando as palavras reservadas signed, unsigned, short e long. Veremos estas palavras reservadas (chamadas de modificadores de intervalo) logo abaixo.
Importante: se você quiser utilizar um número que esteja além dessa faixa de valores, você deve utilizar o tipo double.
double inteiro = 45687921681287; // um inteiro armazenado dentro do tipo double.
[editar] Ponto flutuante (precisão simples)
Representa números fracionários e números reais (o que inclui os números inteiros). A faixa de valores varia de 1,2e-38 até 3,4e+38.
A declaração é feita através da palavra reservada float:
float real = 1.15479637; // poucas casas decimais.
[editar] Ponto flutuante (dupla precisão)
Igualmente ao tipo float, utilizado para números fracionários e reais, porém com precisão ainda maior. A faixa de valores varia de de 2,2e-308 até 1,8e+308.
A declaração é com a palavra reservada double:
double numero = 558749.16846516487975132156857452131274127412974812794812794812798412; // precisão acurada.
[editar] Lógico (booleano)
É o tipo mais simples, e pode armazenar apenas dois valores: verdadeiro ou falso. Para definir esses valores você deve proceder ao seguinte:
bool logico1 = true; // True significa verdadeiro em inglês; bool logico2 = false; // False significa falso em inglês; bool logico3 = 0; // Atribui-se o inteiro 0 (zero) equivale a 'false'; bool logico4 = 189; // Qualquer valor diferente de 0 (zero) é interpretado como 'true';
[editar] Seqüencias de escape
Especialmente no caso de uma variável tipo caracter ou string, você pode querer declarar, por exemplo, a própria aspa simples (utilizando \'), ou um caracter de nova linha (\n). Para isso você deve utilizar seqüências de escape. Todas elas vêm precedidas de uma barra invertida (\).
| Sequência | Equivale a |
|---|---|
| \n | Nova linha |
| \t | Tabulação |
| \b | Backspace |
| \" | Aspa dupla |
| \' | Aspa simples |
| \? | Ponto de interrogação |
| \\ | Barra invertida |
Exemplo:
cout << "Linha 1.\n\n\nLinha depois de três \'quebras de linhas\'." << endl;
A saída no console se pareceria com:
Linha 1. Linha depois de três 'quebras de linha'.
[editar] Modificadores de faixa (ou intervalo)
Em C++ existem alguns modificadores de faixa (ou intervalo). Isso altera a faixa de valores suportada pela variável (mas a capacidade de armazenamento continua a mesma). São eles: short, long, unsigned e signed.
[editar] Short
O modificador short, que é uma palavra reservada da linguagem, fixa a faixa dos valores inteiros (int) para -32.768 até 32.767. Quando você declara um inteiro, ele inicialmente é da faixa de um long int. Se você quer reduzir a faixa, use short int.
short int numero = 12500;
[editar] Long
No subtópico "inteiros", dissemos que a faixa de um número inteiro é de -2.147.483.648 até 2.174.483.647. Na verdade, quando você declara um int, ele já é um long int. Ao declarar um long int, estamos apenas assegurando essa faixa de valores para int.
Long também pode ser utilizado com float. Nesse caso, a faixa de valores de float será a mesma de um double.
long int numero = 45648978; long float numero2 = 4568.0;
[editar] Unsigned e signed
Estes modificadores alteram a faixa dos números inteiros (int) e caracteres (char) para valores somente positivos (unsigned) ou fixa a faixa deles em valores com sinal (signed). Por padrão, se você não declarar esses modificadores, as variáveis tipo inteiro e caracter ASCII serão signed.
Quando definimos que um inteiro terá valores sem sinal (unsigned), na verdade estamos dizendo que só estamos admitindo valores positivos. Isso altera a faixa de um inteiro para 0 até 4.294.967.295 - uma capacidade maior para números positivos). Se você declarar um valor negativo, ocorrerá um estouro de faixa.
Com signed, fixamos a faixa normal para um número inteiro.
unsigned int numero1 = 700; // inteiro sem sinal. signed int numero2 = -700; // admite valores negativos (padrão do tipo int). unsigned short int numero3 = 5000; // pode-se combinar modificadores. signed short int numero4 = 22000; signed long int numero5 = 454658; unsigned long int numero6 = 3000000000; // aceita somente inteiros positivos. signed char caracter1 = -128; // caracter com sinal. Faixa: -128 até 127. unsigned char caracter2 = 255; // caracter sem sinal. Faixa: 0 até 255.
[editar] Estouro de faixa (range overflow)
Se você declarar um valor maior do que o tipo suporta, ocorrerá um estouro de faixa, também chamado range overflow.
Ao chegar no fim da faixa de valores para números inteiros e caracteres (incluindo wchar_t), você volta para o início ou para o fim da faixa. Exemplo:
unsigned long int numero = 4294967295; // o valor máximo para um inteiro longo sem sinal. cout << "Imprime: " << numero << endl; numero = 4294967296; // estouramos a faixa de valores. Voltará ao início da faixa. cout << "Imprime: " << numero << endl; numero = -1; // estouramos novamente a faixa. Neste caso, retornará para o fim da faixa. cout << "Imprime: " << numero << endl;
A saída no console se parecerá com:
Imprime: 4294967295 Imprime: 0 Imprime: 4294967295
Para o tipo double, se você estourar a faixa de valores, ocorrerá um erro de compilação (indicando constant too big ou token overflowed internal buffer). Isso ocorrerá porque para double o número deve "caber" em 64 bits. Tente o seguinte e ocorrerá erro de compilação:
double numero = 18446744073709551616; // 2 elevado a 64. // ERRO. Não compilará.
[editar] Espaço ocupado na memória
As variáveis e constantes são armazenadas na memória e, dependendo do tipo, ocupam um espaço diferente nela. Abaixo segue o tamanho em bytes ocupado por cada tipo e a faixa:
| Sequência | Bytes ocupados | Faixa (ou intervalo) |
|---|---|---|
| short int signed int signed short int wchat_t signed wchar_t |
2 | -32.768 a 32.767 |
| unsigned short int unsigned wchar_t |
2 | 0 a 65.535 |
| int long int signed long int |
4 | -2.147.483.648 até 2.147.483.647 |
| unsigned int unsigned long int |
4 | 0 a 4.294.967.295 |
| char signed char |
1 | -128 a 127 |
| unsigned char | 1 | 0 a 255 |
| bool | 1 | true ou false |
| float | 4 | 1,2e-38 a 3,4e+38 |
| double long float |
8 | 2,2e-308 a 1,8e+308 |
[editar] Sizeof
Sizeof é um operador unário de C++ que é utilizado semelhantemente a uma chamada de função. Sizeof será muito importante em tópicos posteriores, porque retorna o tamanho que um tipo de dados ocupa. Para utilizá-la, a sintaxe é sizeof([tipo/variável/constante/objeto]). Exemplo:
int numero = 50; cout << "Tamanho de um inteiro: " << sizeof(int) << endl; cout << "Tamanho de um double: " << sizeof(double) << endl; cout << "Tamanho da variável número: " << sizeof(numero) << endl;
A saída no console se parecerá com:
Tamanho de um inteiro: 4 Tamanho de um double: 8 Tamanho da variável número: 4
Sizeof é uma palavra reservada da linguagem C++.
[editar] Typedef
Se você quiser abreviar a declaração de um tipo de dados, você pode usar typedef. Esta função também server para criar uma estrutura de dados, que veremos logo abaixo. Typedef é uma palavra reservada da linguagem.
Para abreviar a declaração de um tipo de dados, faça o seguinte:
#include <iostream> using namespace std; typedef unsigned short int ushort; // a partir de agora você pode usar "ushort" para declarar inteiros curtos sem sinal. // typedef não precisa estar no início do código. int main() { ushort numero = 10; // um inteiro curto sem sinal. }
[editar] Conversões
Você pode realizar conversões entre os tipos de dados. Existem dois tipos de conversão: as implícitas e as explícitas. Vejamos cada uma.
[editar] Conversões implícitas
Conversão implícita é aquela realizada pela linguagem sem que o programador tenha explicitamente solicitado que ela a fizesse (entretanto, fica subentendido que você deseja fazer a conversão, por isso ela é chamada de implícita). C++ converte implicitamente a maioria dos tipos de dados. Os principais são:
- De inteiros para caracteres e ponto flutuante.
- De ponto flutuante para inteiro e caracter.
- De caracter para inteiro e ponto flutuante.
Exemplo (de caracter para inteiro e ponto flutuante):
char c = 'a'; int i; float f; double d; i = c; // conversão implícita. i recebe o valor 97 (código de 'a'); f = c; // f recebe 97; d = c; // d recebe 97;
Importante: Quando convertemos ponto flutuante para inteiros e caracteres, a parte fracionária é desprezada. Ao realizar a conversão poderá ocorrer um estouro de faixa, alterando o valor.
[editar] Conversões explícitas
Na conversão implícita você solicita que a linguagem realize a conversão, de forma explícita. C++ converte implicitamente a maioria dos tipos de dados mas, para certificar-se de que fará a conversão correta, você pode fazer conversões explícitas (cast), conforme o exemplo que segue:
char c = 'a'; cout << "Conversão explícita de uma caracter para inteiro: " << (int) c << endl; float f = (float) c; // conversão explícita para ponto flutuante, precisão simples. cout << "Conversão explícita de um caracter para ponto flutuante: " << f << endl;
[editar] Tipos de dados compostos
Os tipos de dados compostos derivam dos tipos primitivos e são úteis para resolver uma grande quantidade de problemas. Se quisermos, por exemplo, armazenar uma frase inteira? Ou, por exemplo, os tempos dos atletas que disputaram uma corrida?
Para resolver esses problemas podemos criar tipos compostos, que atendem a uma função específica no seu programa. Veremos a seguir 4 principais tipos compostos de dados que podemos criar em C++ e que lhe ajudará a solucionar a maioria dos problemas.
[editar] Vetores
float registro[10]; // declaração de um vetor de 10 números reais; registro[0] = 3.50; registro[1] = 3.59; registro[2] = 3.72; registro[9] = 4.84;
No código acima criamos um registro que armazena 10 números de ponto flutuante na memória. Para criar um vetor, declaramos o tipo de dados, o nome do registro e, entre colchetes, a quantidade de registros que queremos armazenar.
Para acessar, utilizamos o nome do registro com o índice do registro entre colchetes. É muito importante ter em mente que os índices começam em 0 (zero)! Sendo assim, um registro de, por exemplo, N coisas, tem índices de 0 até N-1.
A sintaxe para declarar um vetor é:
[tipo] [nome][quantidade de registros (entre colchetes)];
Ao declarar a quantidade de registros, você pode fazê-lo digitando um número inteiro ou colocando ali o nome de uma constante inteira. Por exemplo:
#include <iostream> using namespace std; #define TAM 100 const int TAM2 = 200; int main() { char a[TAM]; // um vetor de caracteres com 100 registros. float b[TAM2]; // um vetor de números reais com 200 registros. cout << "Tamanho do vetor A: " << sizeof(a) << endl; cout << "Tamanho do vetor B: " << sizeof(b) << endl; }
Isso é útil quando criamos vários vetores, todos com o mesmo tamanho. Se, porventura, for necessário modificar o tamanho do vetor, você pode fazê-lo simplesmente alterando o valor da constante.
Você pode utilizar o operador sizeof para obter o tamanho de um registro. A saída no console do código acima geraria a seguinte saída no console:
Tamanho do vetor A: 100 Tamanho do vetor B: 800
[editar] Matrizes
Matrizes são, conceitualmente, um vetor de vetores. A declaração de uma matriz se dá semelhantemente a de um vetor. Ex:
int matriz[10][20]; // uma matriz 10 colunas e 20 linhas. matriz[0][0] = 1; // primeira célula da primeira coluna. matriz[0][1] = 2; // segunda célula da primeira coluna. matriz[9][19]; // última célula da última coluna.
Note que os índices, tanto das colunas quanto das linhas, começam em zero também. O primeiro colchete indica a coluna e o segundo a linha.
Podemos criar matrizes com mais dimensões. Exemplo:
int matriz[5][6][7]; // uma matriz com 210 campos, formada por 5 colunas, cada coluna com 6 linhas, // e cada linha contendo 7 campos.
Todas as regras válidas para vetores valem para matrizes.
[editar] Strings
Strings são vetores de caracteres. Ainda que sejam vetores, as strings em C++ possuem algumas particularidades.
#include <iostream> using namespace std; #include <string> int main() { char frase[50]; // uma string com tamanho fixo: 50 caracteres. strcpy(frase,"Tipos de dados"); // copia "Tipos de dados" para frase. char *frase1; // uma string de tamanho indefinido. frase1 = "Curso de C++"; string frase2; // uma string construída usando a biblioteca <string> frase2 = "Testes com strings"; cout << frase << "\n" << frase1 << "\n" << frase2 << endl; system ("pause"); }
A saída no console se parecerá com:
Tipos de dados Curso de C++ Testes com strings
Aprenderemos a utilizar strings e manipulá-las no tópico Entrada e saída, Biblioteca <string>.
[editar] Structs
Structs, do inglês structure, são utilizadas para criar estruturas de dados: tipos compostos de dados que, em conjunto, formam uma única estrutura.
A declaração e o uso de structs é bastante simples.
#include <iostream> using namespace std; typedef struct Aluno { int matricula; int idade; char* nome; }; // o ponto e vírgula é obrigatório no fim das chaves ao criar uma struct. int main() { Aluno individuo; // cria 1 (uma) variável chamada indivíduo, que tem matrícula, idade e nome. Aluno turma[30]; // cria um vetor de 30 alunos. Um conjunto de alunos é uma turma. individuo.nome = "Fulano"; // altera o atributo nome de 'individuo' para Fulano. individuo.matricula = 12587; // altera o atributo matrícula de 'individuo' para 12587. individuo.idade = 20; // altera o atributo idade de 'individuo' para 20. turma[0].nome = "Primeiro aluno"; turma[0].matricula = 6716; turma[0].idade = 18; cout << "Dados de \'individuo\':\nNome: " << individuo.nome << "\nMatrícula: " << individuo.matricula << "\nIdade: " << individuo.idade << endl; cout << "\nDados do primeiro aluno da turma:\nNome: " << turma[0].nome << "\nMatrícula: " << turma[0].matricula << "\nIdade: " << turma[0].idade << endl; // não havíamos comentado mas, se o código não couber em uma linha, // você pode quebrar para a próxima. O comando termina apenas no ';'. system("pause"); }
A saída no console se parecerá com:
Dados de 'indivíduo': Nome: Fulano Matrícula: 12587 Idade: 20 Dados do primeiro aluno da turma: Nome: Primeiro aluno Matrícula: 6716 Idade: 18
A declaração da struct não precisa estar, necessariamente, no início do código (antes da main). Poderá estar dentro de um bloco de chaves ({}), mas nesse caso seria como uma struct local: só poderia ser acessada dentro deste bloco. Pelo código podemos notar a sintaxe da declaração de uma struct:
typedef struct [NOME] {
<variáveis>
}; - o ponto-e-vírgula depois da chave é fundamental.
Para acessar os atributos, a sintaxe é a seguinte:
[nome da estrutura].[nome do atributo] = [valor] (para modificar os valores) [nome da estrutura].[nome do atributo] (para acessar os valores)
Note que podem ser criados vetores e matrizes de structs. Isso é útil, quando, como no exemplo dado, queremos criar o registro de alunos (de uma turma), por exemplo.
[editar] Revisão
Neste tópico discutimos os tipos de dados possíveis em C++: os tipos primitivos e os compostos. Entretanto, podemos criar tipos e estruturas de dados através de classes, mas este assunto será abordado inteiramente em um tópico posterior.
Antes de passar para o próximo tópico, certifique-se de ter compreendido e memorizado o que segue:
- A sintaxe da declaração e acesso aos tipos primitivos de dados.
- Os modificadores de acesso: o que eles fazem e a faixa para qual alteram cada tipo de dados.
- O que é estouro de faixa (range overflow), e o que acontece quando isso ocorre.
- A função do operador unário sizeof.
- Os erros comuns que geram erro de compilação, citados nos comentários dos códigos.
- O que é conversão implícita e explícita e como fazê-las.
- O que são tipos compostos de dados, e a sintaxe da declaração e acesso a vetores, matrizes e strings.
- Structs: o que são, a declaração e o acesso aos atributos.