Quando aprendi a linguagem C ++ pela primeira vez, aprendi que além de int, float etc., existiam versões menores ou maiores desses tipos de dados na linguagem. Por exemplo, eu poderia chamar uma variável x
int x;
or
short int x;
A principal diferença é que short int ocupa 2 bytes de memória, enquanto int ocupa 4 bytes, e short int possui um valor menor, mas também podemos chamar isso para torná-lo ainda menor:
int x;
short int x;
unsigned short int x;
o que é ainda mais restritivo.
Minha pergunta aqui é se é uma boa prática usar tipos de dados separados de acordo com os valores que sua variável assume dentro do programa. É uma boa ideia sempre declarar variáveis de acordo com esses tipos de dados?
c++
data-structures
Bugster
fonte
fonte
unsigned
alguma forma faz com que um número inteiro ocupe menos espaço, o que é obviamente falso. Ele terá a mesma contagem de valores representáveis discretos (dar ou receber 1, dependendo de como o sinal é representado), mas apenas mudando exclusivamente para o positivo.Respostas:
Na maioria das vezes, o custo do espaço é insignificante e você não deve se preocupar com isso; no entanto, você deve se preocupar com as informações extras que você está fornecendo ao declarar um tipo. Por exemplo, se você:
Você está fornecendo uma informação útil a outro desenvolvedor: o salário não pode ser negativo.
A diferença entre short, int, long raramente causa problemas de espaço no seu aplicativo. É mais provável que você acidentalmente suponha que um número sempre caiba em algum tipo de dados. Provavelmente é mais seguro sempre usar int, a menos que você tenha 100% de certeza de que seus números sempre serão muito pequenos. Mesmo assim, é improvável economizar uma quantidade notável de espaço.
fonte
unsigned
neste caso é uma má ideia: não apenas o salário não pode ser negativo, mas a diferença entre dois salários também não pode ser negativa. (Em geral, usar unsigned para nada, mas bit-twiddling e ter comportamento definido no estouro é uma má idéia.)O OP não disse nada sobre o tipo de sistema para o qual está escrevendo programas, mas presumo que o OP estivesse pensando em um PC típico com GB de memória, já que o C ++ é mencionado. Como um dos comentários diz, mesmo com esse tipo de memória, se você tiver vários milhões de itens de um tipo - como uma matriz -, o tamanho da variável poderá fazer a diferença.
Se você entrar no mundo dos sistemas embarcados - o que não está realmente fora do escopo da questão, já que o OP não o limita aos PCs -, o tamanho dos tipos de dados é muito importante. Acabei de terminar um projeto rápido em um microcontrolador de 8 bits que possui apenas 8K palavras de memória de programa e 368 bytes de RAM. Lá, obviamente, cada byte conta. Nunca se usa uma variável maior do que o necessário (tanto do ponto de vista do espaço quanto do tamanho do código - os processadores de 8 bits usam muitas instruções para manipular dados de 16 e 32 bits). Por que usar uma CPU com recursos tão limitados? Em grandes quantidades, eles podem custar apenas um quarto.
Atualmente, estou fazendo outro projeto incorporado com um microcontrolador de 32 bits baseado em MIPS que possui 512K bytes de flash e 128K bytes de RAM (e custa cerca de US $ 6 em quantidade). Como em um PC, o tamanho "natural" dos dados é de 32 bits. Agora, torna-se mais eficiente, em termos de código, usar ints para a maioria das variáveis, em vez de caracteres ou curtos. Mais uma vez, porém, qualquer tipo de matriz ou estrutura deve ser considerado se tipos de dados menores são necessários. Ao contrário de compiladores para sistemas maiores, é mais provável variáveis em uma estrutura vai ser embalado em um sistema embarcado. Tomo o cuidado de sempre tentar colocar todas as variáveis de 32 bits primeiro, depois 16 e 8 bits para evitar "buracos".
fonte
A resposta depende do seu sistema. Geralmente, aqui estão as vantagens e desvantagens do uso de tipos menores:
Vantagens
Desvantagens
Meu conselho é assim:
Como alternativa, você pode usar the
int_leastn_t
ouint_fastn_t
from stdint.h, onde n é o número 8, 16, 32 ou 64.int_leastn_t
type significa "Quero que sejam pelo menos n bytes, mas não me importo se o compilador o alocar como um tipo maior para se adequar ao alinhamento ".int_fastn_t
significa "Eu quero que isso tenha n bytes de comprimento, mas se o código for executado mais rapidamente, o compilador deve usar um tipo maior que o especificado".Geralmente, os vários tipos stdint.h são muito melhores práticas do que simples
int
, etc, porque são portáteis. A intençãoint
era não fornecer uma largura especificada apenas para torná-lo portátil. Mas, na realidade, é difícil portar porque você nunca sabe o tamanho que será em um sistema específico.fonte
Dependendo de como o sistema operacional específico funciona, geralmente você espera que a memória seja alocada sem otimização, de modo que, quando você pede um byte, uma palavra ou outro tipo de dado pequeno a ser alocado, o valor ocupa um registro inteiro. próprio. Como seu compilador ou intérprete trabalha para interpretar isso, no entanto, é outra coisa; portanto, se você compilar um programa em C #, por exemplo, o valor poderá ocupar fisicamente um registro para si mesmo; no entanto, o valor será verificado nos limites para garantir que você não tente armazenar um valor que excederá os limites do tipo de dados pretendido.
Em termos de desempenho, e se você é realmente pedante sobre essas coisas, provavelmente é mais rápido simplesmente usar o tipo de dados que mais se aproxima do tamanho do registro de destino, mas você perde todo esse adorável açúcar sintático que facilita o trabalho com variáveis .
Como isso te ajuda? Bem, depende de você decidir que tipo de situação você está codificando. Para quase todos os programas que já escrevi, basta confiar no seu compilador para otimizar as coisas e usar o tipo de dados que é mais útil para você. Se você precisar de alta precisão, use os tipos de dados maiores de ponto flutuante. Se estiver trabalhando apenas com valores positivos, provavelmente você poderá usar um número inteiro não assinado, mas, na maioria das vezes, basta usar o tipo de dados int.
Se, no entanto, você tiver alguns requisitos de dados muito rígidos, como escrever um protocolo de comunicação ou algum tipo de algoritmo de criptografia, o uso de tipos de dados com intervalo verificado pode ser muito útil, principalmente se você estiver tentando evitar problemas relacionados a excedentes / subunidos de dados ou valores de dados inválidos.
A única outra razão pela qual posso pensar em detalhes para usar tipos de dados específicos é quando você está tentando comunicar a intenção dentro do seu código. Se você usar uma abreviação, por exemplo, estará dizendo a outros desenvolvedores que está permitindo números positivos e negativos dentro de um intervalo de valores muito pequeno.
fonte
Como comentou scarfridge , este é um
Tentar otimizar o uso da memória pode afetar outras áreas de desempenho, e as regras de ouro da otimização são:
Para saber se agora é a hora de otimizar, é necessário fazer comparações e testes. Você precisa saber onde seu código está sendo ineficiente, para poder direcionar suas otimizações.
Para determinar se a versão otimizada do código é realmente melhor do que a implementação ingênua a qualquer momento, você precisa compará-las lado a lado com os mesmos dados.
Além disso, lembre-se de que apenas porque uma determinada implementação é mais eficiente na geração atual de CPUs, não significa que sempre será assim. Minha resposta à pergunta A micro-otimização é importante ao codificar? detalha um exemplo da experiência pessoal em que uma otimização obsoleta resultou em uma desaceleração em ordem de magnitude.
Em muitos processadores, os acessos à memória não alinhados são significativamente mais caros do que os acessos à memória alinhados. A inserção de alguns curtos em sua estrutura pode significar apenas que seu programa deve executar a operação de empacotar / descompactar toda vez que você tocar em qualquer valor.
Por esse motivo, os compiladores modernos ignoram suas sugestões. Como comenta nikie :
Segundo, adivinhe o seu compilador por sua conta e risco.
Há um lugar para essas otimizações, ao trabalhar com conjuntos de dados de terabytes ou microcontroladores incorporados, mas para a maioria de nós, isso não é realmente uma preocupação.
fonte
Isto está incorreto. Você não pode fazer suposições sobre quantos bytes cada tipo contém, além de
char
ter um byte e pelo menos 8 bits por byte, além de o tamanho de cada tipo ser maior ou igual ao anterior.Os benefícios de desempenho são incrivelmente minúsculos para as variáveis de pilha - elas provavelmente serão alinhadas / preenchidas de qualquer maneira.
Por causa disso,
short
elong
praticamente não há uso hoje em dia, e você quase sempre está melhor usandoint
.Claro, também há o
stdint.h
que é perfeitamente bom de usar quandoint
não o corta. Se você estiver alocando enormes matrizes de números inteiros / estruturas, entãointX_t
faz sentido, pois você pode ser eficiente e confiar no tamanho do tipo. Isso não é prematuro, pois você pode economizar megabytes de memória.fonte
long
pode ser diferenteint
. Se o seu compilador for LP64,int
tiver 32 bits elong
64 bits, você descobrirá queint
s ainda podem estar alinhados em 4 bytes (meu compilador, por exemplo).int64_t
int32_t
,,int_fast32_t
elong
são todas boas opções,long long
é apenas um desperdício eint
não é portátil.Isso será de um tipo de ponto de vista de OOP e / ou empresa / aplicativo e pode não ser aplicável em certos campos / domínios, mas eu meio que quero trazer à tona o conceito de obsessão primitiva .
É uma boa ideia usar diferentes tipos de dados para diferentes tipos de informações em seu aplicativo. No entanto, provavelmente NÃO é uma boa ideia usar os tipos internos para isso, a menos que você tenha alguns problemas sérios de desempenho (que foram medidos e verificados e assim por diante).
Se quisermos modelar temperaturas em Kelvin em nosso aplicativo, PODEMOS usar um
ushort
ouuint
algo semelhante para denotar que "a noção de graus negativos Kelvin é absurda e um erro de lógica de domínio". A idéia por trás disso é sólida, mas você não está indo até o fim. O que percebemos é que não podemos ter valores negativos, por isso é útil podermos obter o compilador para garantir que ninguém atribua um valor negativo a uma temperatura Kelvin. Também é verdade que você não pode executar operações bit a bit em temperaturas. E você não pode adicionar uma medida de peso (kg) a uma temperatura (K). Mas se você modelar temperatura e massa comouint
s, podemos fazer exatamente isso.O uso de tipos internos para modelar nossas entidades DOMAIN provavelmente causará algum código confuso, verificações perdidas e invariantes quebrados. Mesmo que um tipo capture ALGUM parte da entidade (não pode ser negativo), ele provavelmente perderá outras (não pode ser usado em expressões aritméticas arbitrárias, não pode ser tratado como uma matriz de bits etc.)
A solução é definir novos tipos que encapsulam os invariantes. Dessa forma, você pode ter certeza de que dinheiro é dinheiro e distâncias são distâncias, e não pode adicioná-las, e não pode criar uma distância negativa, mas PODE criar uma quantidade negativa de dinheiro (ou uma dívida). Obviamente, esses tipos usarão os tipos internos internamente, mas isso está oculto para os clientes. Em relação à sua pergunta sobre desempenho / consumo de memória, esse tipo de coisa pode permitir que você altere como as coisas são armazenadas internamente, sem alterar a interface de suas funções que operam nas entidades do seu domínio, caso você descubra isso, a
short
é demais ampla.fonte
Sim, claro. É uma boa idéia usar
uint_least8_t
para dicionários, matrizes constantes enormes, buffers etc. É melhor usaruint_fast8_t
para fins de processamento.uint8_least_t
(armazenamento) ->uint8_fast_t
(processamento) ->uint8_least_t
(armazenamento).Por exemplo, você está usando o símbolo de 8 bits
source
, códigos de 16 bitsdictionaries
e 32 bitsconstants
. Do que você está processando operações de 10 a 15 bits com elas e produz 8 bitsdestination
.Vamos imaginar que você precise processar 2 gigabytes de
source
. A quantidade de operações de bits é enorme. Você receberá um ótimo bônus de desempenho se mudar para tipos rápidos durante o processamento. Tipos rápidos podem ser diferentes para cada família de CPU. Você pode incluirstdint.h
e usaruint_fast8_t
,uint_fast16_t
,uint_fast32_t
, etc.Você poderia usar em
uint_least8_t
vez deuint8_t
portabilidade. Mas ninguém sabe realmente qual CPU moderna usará esse recurso. A máquina VAC é uma peça de museu. Então talvez seja um exagero.fonte