Programar em C/Tipos de dados: diferenças entre revisões

[edição verificada][edição verificada]
Conteúdo apagado Conteúdo adicionado
Abacaxi (discussão | contribs)
Sem resumo de edição
Abacaxi (discussão | contribs)
Sem resumo de edição
Linha 1:
Até agora você só viu as variáveis do tipo <code>int</code>, que servem para guardar números inteiros. A linguagem C tem outros tipos fundamentais. São eles:
{{er|Movido para [[C/Variáveis#Tipos_de_dados]] para facilitar a fusão no futuro.|[[Usuário:Abacaxi|Abacaxi]] ([[Usuário Discussão:Abacaxi|Discussão]]) 18h14min de 1 de janeiro de 2014 (UTC)}}
* <code>int</code>, para números inteiros entre -2147483647 e 2147483647, utiliza 4 bytes;
{{fusão|Programar_em_C/Variáveis#Tipos_de_vari.C3.A1veis}}
* <code>char</code>, para caracteres individuais do padrão ASCII, utiliza 1 byte;
{{reciclagem}}
* <code>float</code>, para reais entre (aproximadamente) 10<sup>-38</sup> e 10<sup>38</sup>, utiliza 4 bytes, precisão de 7 dígitos;
 
* <code>double</code>, para reais entre (aproximadamente) 10<sup>-4932</sup> e 10<sup>4932</sup>, utiliza 8 bytes, precisão de 15 dígitos;
== Tipos de memória ==
* <code>bool</code>, para indicar true (verdadeiro) ou false (falso), utiliza 1 byte; Presente apenas no padrão C99 em diante.
Existem 3 tipos principais de locais de memória no computador:
*'''The central processing unit (CPU) – cache memory'''
**A CPU para além da coordenação das operações do computador tem um tipo de memória que se chama Cache memory. É nesta memória existe um segmento a que se dá o nome de register, que é usada frequentemente para guardar as instruções e dados mais frequentes.
**A CPU pode acessar a esta memória muito rapidamente
**No entanto não se consegue guardar muita memória, ie, muitos dados, ela é pequena daí se usar o RAM
**A memória é temporária (as instruções e dados são perdidos na ausência de energia)
*'''Random access memory (RAM)'''
**O cpu consegue aceder aos dados do ram quase tão rapidamente como ao cache.
**A memória ram é maior do que a cache, ie, consegue-se guardar mais dados.
**No entanto a memória é temporária (as instruções e dados são perdidos na ausência de energia)
*'''Persistent storage'''
**Chama-se a esta memória persistente porque ela permanece mesmo após ausência de energia, ie, se desligarmos da corrente e a voltarmos a ligar conseguimos aceder novamente aos dados.
**Exemplos são o hard drive, floppy disk, zip disk, optical drive...
**Têm mais capacidade do que o ram e o cache, ie, conseguem guardar mais informação
**No entanto um programa de computador não consegue executar instruções localizadas no armazenamento persistente. os dados e instruções têm de ser loaded para o ram.
por isso geralmente se fala, ie, se focaliza a conversa da memoria sobre o RAM, e não tanto nos outros tipos de memória
*Ainda temos os conceitos de:
**Heap
**Buffer
**Registrador. Contém dados específicos para arithmetic and logic Unit (ALU).
Para não entrar em extremos detalhes, antes do tempo, vamos assumir que estamos sempre a trabalhar em RAM, até algo em contrário. Além de que até é bastante verdade, pois todos os dados passam por ela.
 
16-bit CPU pode processar 16 bits ou 2 bytes ao mesmo tempo. Um 64-bit CPU pode processar 8 bytes ao mesmo tempo.
 
== Explicando Bits e Bytes ==
 
=== Explicando bits e bytes ===
Podemos pensar na memória do computador como uma fita, uma grande fita feita de frames sequenciais.
 
Linha 47 ⟶ 23:
 
E na verdade podemos estender este conceito para um número infinito de combinações.
 
 
Ora o que aconteceu é que nos bastavam pouco menos de 256 combinações (8 bits ordenados) para termos uma combinação para cada letra, maiúscula e minúscula, número, pontos de exclamação, interrogação, etc. …e isso era o suficiente para a nossa comunicação. Mas para haver um certo consenso para que uma dada combinação desse um dado símbolo surgiu a tabela ASCII (surgiram outras tabelas quando se quis colocar os símbolos de outras línguas, como o japonês ou o chinês – ver tabela ISO)
Portanto com 8 bits ou 8 casas conseguíamos ter qualquer símbolo que utilizamos. A esse conjunto de 8 bits chamamos de byte, mais convenientemente. Portanto, um byte tem 8 casas de zeros /uns , ou seja 2 elevado a 8 dá as 256 combinações. E o byte é a unidade básica que o C++ consegue operar e é representado pelo tipo char.
 
=== TextoNúmeros inteiros == =
Se dissermos que 2 bytes representam inteiros, poderemos utilizar as 65 536 combinações, pois 2 bytes -16bits- temos 2 elevado a 16 = 65 536 e isso dar-nos-ia esses números todos. Assim se quisermos apenas os positivos com o zero temos de [0, 65535].
(tipo de dado) (char)
Temos o char que é um caracteres da tabela ASCII, que ocupa um byte, logo temos 256 combinações. Este nº de combinações dá para termos todo o alfabeto, minúsculas e maiúsculas, os algarismos de 0 a 9 e todas as marcas de pontuação tipo :.;!?, etc
 
Se quisermos ter números negativos e positivos podemos dividir esse valor a meio e dá 32768 para cada lado positivo e negativo, mas como temos de ter o zero vamos roubar um valor ao lado positivo e então ficamos com o intervalo [-32768, 32767]. E ficamos com as mesmas 65 536 combinações.
== Números inteiros ==
 
Apresentamos inteiro com 2 bytes, mas eles podem ter 4 bytes, isso vai depender do processador do computador, ie, com quantos bytes consegue ele lidar ao mesmo tempo.
Mas agora observem o ponto: com a tipologia char apenas podemos expressar dois dígitos com dois bytes certo. (cada byte-um digito(número))
 
Também existem outros tipos, como short (ou short int), que serve para inteiros menores, long (ou long int) para inteiros maiores. Qualquer tipo inteiro pode ser precedido por unsigned (o signed para COM negativos), para cortar os números negativos, permitindo maior capacidade de armazenamento de números positivos. Alguns compiladores aceitam o long long, para aumentar ainda mais o tamanho da variável, alguns desses só aceitam para o tipo int, outros também para o tipo double.
Mas se dissermos que aqueles 2 bytes são ints. já poderemos utilizar as 65 536 combinações pois 2 bytes -16bits- temos 2 elevado a 16 = 65 536 e isso dar-nos-ia esses números todos.
 
Podemos alterar a maneira como os dados são guardados com os '''modificadores de tipo'''. Você pode modificar os tipos de duas maneiras.
Assim se quisermos apenas os positivos com o zero temos de [0, 65535]
 
==== Tamanho: <code>short</code> e <code>long</code> ====
Se quisermos ter números negativos e positivos podemos dividir esse valor a meio e dá 32768 para cada lado positivo e negativo, mas como temos de ter o zero vamos roubar um valor ao lado positivo e então ficamos com o intervalo [-32768, 32767]. e ficamos com as mesmas 65 536 combinações.
Você pode modificar o tamanho de uma variável usando os '''modificadores de tipo''', que são dois: <code>short</code> e <code>long</code>. Note que <code>float</code> e <code>char</code> não podem ser modificados em tamanho.
 
* '''<code>short</code>''' diminui o espaço necessário para guardar a variável (diminuindo também a gama de valores que esta pode assumir). Só pode ser usado com <code>int</code>.
eu apresentei o int com 2 bytes, mas eles podem ter 4 bytes, isso vai depender do processador do computador, ie, com quantos bytes consegue ele lidar ao mesmo tempo. para para desenvolver este tema ver sistema operativo
* '''<code>long</code>''' aumenta o espaço tomado pela variável, e portanto aumenta seu valor máximo e/ou sua precisão. Pode ser usado com <code>int</code> e <code>double</code>.
* O padrão C de 1999 adicionou um terceiro modificador, suportado pelos compiladores mais recentes, inclusive o gcc: <code>long long</code>, que aumentaria ainda mais a capacidade da variável. Alguns deles suportam esse modificador apenas para o tipo <code>int</code>, e outros suportam também para <code>double</code>.
 
Uma observação é necessária: segundo o padrão, não existe nenhuma garantia de que uma variável <code>short int</code> é menor que uma variável <code>int</code>, nem que <code>long int</code> é maior que <code>int</code>. Apenas é garantido que <code>int</code> não é maior que <code>long</code> nem menor que <code>short</code>. De fato, nos sistemas x86 de 32 bits (ou seja, a maioria dos computadores pessoais atualmente), o tamanho de <code>int</code> é igual ao de <code>long</code>. Geralmente, <code>int</code> será o tamanho nativo do processador &mdash; ou seja, 32 bits num processador de 32 bits, 16 bits num processador de 16 bits etc.
Aqui está um quadro com as variantes do int: signed/insigned; short/long. isto são os specifiers ou modifiers, porque alteram especificam de acordo com a necessidade
 
==== Sinal: <code>signed</code> e <code>unsigned</code> ====
Table 2-2: Whole Number Data Types, Sizes, and Ranges
Existe outro tipo de modificador, que define se o número vai ser guardado com '''sinal''' ou não. São os modificadores <code>signed</code> e <code>unsigned</code>, suportados pelos tipos inteiros apenas.
 
* '''<code>signed</code>''' diz que o número deve ser guardado com sinal, ou seja, serão permitidos valores positivos e negativos. Esse é o padrão, portanto esse modificador não é muito usado.
{| class=wikitable
* '''<code>unsigned</code>''' diz que o número deve ser guar
| Data Type || Size (in Bytes) || Range (in E notation)
|-
| short || 2 || –32,768 to 32,767
|-
| unsigned short || 2 || 0 to 65,535
|-
| int || 4 || –2,147,483,648 to 2,147,483,647
|-
| unsigned int || 4|| 0 to 4,294,987,295
|-
| long || 4|| –2,147,483,648 to 2,147,483,647
|-
| unsigned long || 4|| 0 to 4,294,987,295
 
==== Tabela de tipos inteiros ====
|}
Convém ver a tabela de tipos inteiros.
 
Tipo Num de bits Formato para leitura com scanf Intervalo
'''Nota''': O tipo '''long''' é 32 bits como int em computadores de arquitetura 32 bits e 64 bits em computadores de arquitetura 64 bits.
Inicio Fim
char 8 %c -128 127
unsigned char 8 %c 0 255
signed char 8 %c -128 127
int 16 %i -32.768 32.767
unsigned int 16 %u 0 65.535
signed int 16 %i -32.768 32.767
short int 16 %hi -32.768 32.767
unsigned short int 16 %hu 0 65.535
signed short int 16 %hi -32.768 32.767
long int 32 %li -2.147.483.648 2.147.483.647
signed long int 32 %li -2.147.483.648 2.147.483.647
unsigned long int 32 %lu 0 4.294.967.295
 
== Números de ponto flutuante ==
 
'''Nota''': O tipo '''long''' é 32 bits como int em computadores de arquitetura 32 bits e 64 bits em computadores de arquitetura 64 bits.
Os números de ponto flutuante são uma tentativa para guardar valores reais e tipo os fraccionários. Ao contrário dos números reais, os números representáveis pelo hardware são finitos.
 
A maneira como os tipos de ponto flutuante são armazenados é abstrata para o programador, entretanto, o hardware segue o padrão IEEE 754(Standard for Floating-Point Arithmetic).
 
 
'''Pergunta''': Como é que os números de ponto flutuantes são armazenados? e nesse caso como fazer para os fraccionários infinitos?
 
Table 2-3: Floating-point Number Data Types, Sizes, and Ranges
 
{| class=wikitable
| Data Type || Size (in Bytes) || Range (in E notation)
|-
| float || 4 || ±3.4E-38 to ±3.4E38
|-
| double || 8 || ±1.7E-308 to ±1.7E308
|}
 
=== Números de ponto flutuante ===
Os números de ponto flutuante são uma tentativa para guardar números reais, como 3,1415 (pi), -2,3333, 0,00015, 6,02 &times; 10<sup>23</sup>. Ao contrário dos números reais, os números representáveis pelo hardware são finitos. A maneira como os tipos de ponto flutuante são armazenados é abstrata para o programador, entretanto, o hardware segue o padrão IEEE 754 (Standard for Floating-Point Arithmetic).
 
O armazenamento é feito usando notação científica binária.
 
Table 2-4: Scientific and E Notation Representations of Floating Point Values
 
{| class=wikitable
| Decimal Notation || Scientific Notation || E Notation
Linha 123 ⟶ 86:
|-
| 1,200,000,000 || 1.2 x 109 || 1.2E9
|}
 
Os tipos <code>float</code> e <code>double</code> servem para guardar números de ponto flutuante. A diferença entre os dois é, além do intervalo de dados, a precisão. Geralmente, o tipo <code>float</code> guarda dados (com sinal positivo ou negativo) de 3,4E-38 a 3,4E+38 (além do zero). Já <code>double</code> suporta números tão pequenos quanto 1,7E-308 e no máximo 1,7E+308.
 
float 32 %f 3,4E-38 3.4E+38
== Bool ==
double 64 %lf 1,7E-308 1,7E+308
long double 80 %Lf 3,4E-4932 3,4E+4932
 
'''Nota''': O tipo '''long double''' é específico de máquinas X86.
 
dado '''sem sinal'''. Com isso, o valor máximo da variável aumenta, já que não teremos mais valores negativos. Por exemplo, com uma variável <code>char</code> podemos guardar valores de -128 a 127, mas com uma variável <code>unsigned char</code> pode guardar valores de 0 a 255.
 
Para usar esses modificadores, devemos colocá-los '''antes''' do nome do tipo da variável, sendo que o modificador de sinal deve vir antes do modificador de tamanho caso ambos sejam usados. Por exemplo:
 
'''unsigned char''' c;
'''short int''' valor;
'''unsigned long int''' resultado;
 
<div style="background-color: #ddffdd; padding: 10px; margin: 20px;">'''Nota:''' Você pode abreviar <code>short int</code> e <code>long int</code> para simplesmente <code>short</code> e <code>long</code>, respectivamente.</div>
 
=== Bool ===
Este tipo surgiu porque muitas vezes apenas se quer ter 2 valores: sim/não ; verdadeiro/falso.
Tem o tamanho de um byte e tem apenas dois valores 0 e 1 que corresponde a true e false.
 
'''Pergunta''': Por que guardar um bool num byte quando se pode utilizar apenas um bit? A razão é que o computador usa no mínimo o byte, não o bit.
*A razão é que o computador usa no mínimo o byte não o bit.
 
=== Endereços ===
Quando queremos guardar um valor o sistema operativo vai reservar uma quantidade x de bytes, numa posição dada na memória.
A quantidade reservada vai depender do que declararmos, ie do tipo de dados, que queremos guardar. Recorde-se que gravar o valor 5 é diferente de ter 5,67. Por isso é natural que ocupe mais espaço em termos de memória (é natural que quanto maior for o número seja necessário mais combinações de bits e bytes).
Portanto vou necessitar de apenas 1 byte para guardar uma letra que denominamos de char.
Vou necessitar 4 bytes para poder ter de 0 to 4294967295.
 
== Endereços ==
Os vários locais na memória são identificados por um address, que tem uma lógica sequencial numerada. São necessários 16 bits para guardar o endereço de um byte. dito de outra forma são necessários 2 bytes para guardar a morada de um byte.
será isto verdade?!! isso quer dizer que se guardarmos os endereços de todos os bytes, só temos 1/3 da memória disponível para guardar valores. Bem isto é um pouco estanho, mas repare-se que apenas vamos guardar os addresses das variáveis reservadas. Depois as variáveis nem sempre são de 1 byte, por isso apenas iremos guardar o endereço do primeiro byte e não de todos. por fim faz sentido guardar o endereço de outro endereço?
Linha 147 ⟶ 120:
*Sim. Com dois bytes o número de combinações é 256*256.
 
'''perguntaPergunta''': Qual a razão do computador usar apenas bytes como medida mínima? Será que não seria possível utilizar 7 bits ou 5 bits?
*Não é possível pelo fato do computador só entender 0 e 1 então no caso é impossível se ter um número ímpar de bits porque tudo tem que ter o 0 e o 1 por isso que tudo na informática evolui multiplicando-se por 2 (32, 64, 256, 512)
 
=== Compatibilidade de dados na atribuição de valor ===
Se tentarmos colocar um valor diferente do tipo esperado da variável? Temos um problema de compatibilidade de dados:
 
* '''Caso 1''': Declaramos um int e colocamos uma letra
*:Aqui não teremos problemas. Os literais de caracteres são, nativamente, do tipo ''int''. O resultado será um inteiro que contém o valor ASCII do caractere dado.
 
* '''Caso 2''': Declaramos um int e colocamos uma string (sequência de caracteres)
*:Aqui teremos um erro de compilação, em que nos diz que não conseguimos converter "<tt>const char [5]</tt>" em "<tt>int</tt>". Perceba com isso que o compilador tem alguns sistemas de conversão ― note o caso 3.
 
* '''Caso 3''': Declaramos um int e colocamos um float
*:Neste caso, se colocarmos <tt>77.33</tt>, irá ser apenas guardado o valor <tt>77</tt>, perdendo-se a parte decimal.
 
* '''Caso 4''': ''overflow'' ― declaramos um ''short'' e colocamos um valor maior que o máximo
*:Lembre-se que o tipo ''short'' guarda valores de –32767 a 32767. Se colocarmos 32768 (e o compilador não estender esses limites), ''não'' vai acontecer nenhum erro de compilação; o que resulta é que vai ser impresso um número negativo, –32767 (ou, como é comum em vários compiladores, –32768). A lógica disto tem a ver com a maneira como o computador guarda números negativos. Mas também podemos fazer uma analogia com as horas. Imaginemos que vamos somar 6 horas com 7 horas. O resultado seria 13, mas como não existe 13 no relógio, iríamos dar a volta nas horas e chegar ao 1. Assim o resultado será 1.
 
* '''Caso 5''': ''underflow'' ― declaramos um ''short'' e colocamos um valor inferior ao mínimo possível.
*:Aqui temos exactamente a mesma lógica do caso de overflow, mas desta vez é excedido o limite inferior e não o superior.
 
* '''Caso 6:''' declaramos um <tt>unsigned int</tt> e colocamos um número negativo
*:O que acontece aqui é semelhante a um underflow. Mas o que ocorre é que o número é guardado como seria se fosse um <tt>int</tt> comum, negativo. O que muda na prática é a interpretação desse número, de acordo com o tipo de dado que lhe está atribuído. Se tentarmos lê-lo como um <tt>unsigned int</tt>, obteremos um valor positivo obtido pela mesma lógica do overflow/underflow; se o lermos como um (signed) <tt>int</tt>, obteremos o mesmo valor negativo que lhe atribuímos.
 
=== Converter um tipo de variável ===
A conversão de uma variável consiste em converter o tipo de uma variável em um outro. Imagine que você esteja trabalhando com uma variável do tipo float e por alguma razão queira eliminar os números que estão depois da vírgula.
 
Esta operação pode ser realizada de duas maneiras.<br>
'''Conversões do tipo implícita:''' Consiste em uma modificação do tipo de variável que é feita automaticamente
pelo compilador.
:Ex:
int x;
x = 7.123;
 
'''Conversões do tipo explícita:''' Também chamada de operação '''cast''', consiste em forçar a modificação
do tipo de variável usando o operador cast "( )".
:Ex:
int y;
y = (int)7.123;
 
Veja um exemplo da conversão de tipo inteiro em caracteres. Aqui convertemos um numero decimal em um caractere ASCII.
<syntaxhighlight lang="C">
#include <stdio.h>
 
int main(void)
{
int y = 65;
char x;
 
x = (char) y;
printf("O numero inteiro: %d \n O caracter: %c \n\n", y, x);
}
</syntaxhighlight>
 
=== Literais ===
Em programação, um '''literal''' é uma notação que representa um valor constante. Exemplos de literais em C são 415, 19.52, 'C', "João". Esses exemplos representam os quatro tipos de literais em C: literais de inteiros, literais de reais, literais de caracteres e literais de strings. Só com esses exemplos já é possível deduzir como se usam os literais; mas é importante fazer algumas observações:
 
* Literais de inteiros podem ser especificados nas bases decimal, octal ou hexadecimal. Se o literal for prefixado com "0x" ou "0X", ele será interpretado como hexadecimal; se o prefixo for apenas "0", será interpretado como octal; ou se não houver prefixo, será interpretado como decimal.
* Literais de reais podem ser especificados na forma decimal (144.57) ou em notação científica (1.4457e+2). Lembre-se que o separador decimal é o ponto e não a vírgula, como seria usual.
* Literais de caracteres devem vir entre aspas simples (') e conter a representação de apenas um caractere<sup>1</sup>. Usos válidos seriam: 'c', '\n', '\x1b', '\033'. Se você quiser usar a aspa simples como caractere, preceda-a com uma barra invertida: <code>'<nowiki>\'</nowiki>'</code>.
* Literais de strings devem vir entre aspas duplas ("). Para usar aspas duplas dentro de strings, preceda-as com barra invertida: <code>"Ele disse \"Olá\"."</code>. Note que um literal de string adiciona o caractere nulo (<code>\0</code>) ao final da string, pois ele é, em C, a maneira de delimitar o final de uma string.
 
Na verdade, segundo o padrão C, literais de caracteres podem conter a representação de mais um caractere, mas o uso deles seria para representar números e não sequências de caracteres; é um aspecto pouco utilizado da linguagem C.
 
{{AutoCat}}