Cada tipo numérico precisa de uma quantidade específica de bytes em memória para ser representado. Quanto maior a quantidade de bytes demandada, maior o range de valores suportados.
#include int main() { std::cout << "sizeof(char) : " << sizeof(char) << std::endl; std::cout << "sizeof(short) : " << sizeof(short) << std::endl; std::cout << "sizeof(int) : " << sizeof(int) << std::endl; std::cout << "sizeof(long) : " << sizeof(long) << std::endl; std::cout << "sizeof(long long) : " << sizeof(long long) << std::endl; std::cout << "Range for int : " << std::numeric_limits<int>::min() << " - " << std::numeric_limits<int>::max() << std::endl; std::cout << "Range for long: " << std::numeric_limits<long>::min() << " - " << std::numeric_limits<long>::max() << std::endl; }
É interessante notar, entretanto, que, por motivos históricos, a Microsoft escolheu manter, em C++, o tipo long
com 4 bytes, mesmo em sistemas de 64 bits. Isso significa que, modernamente, em C++, int
e long
demandam a mesma quantidade de bytes e suportam o mesmo range de valores.
Sabendo que cada tipo demanda uma quantidade de bytes diferente, seria natural assumir que esses tipos ocupassem quantidades diferentes de memória na stack.
#include <iostream> void doubleIt( char a, short b, int c, long long d, char* da, short* db, int* dc, long long* dd ) { *da = a + a; *db = b + b; *dc = c + c; *dd = d + d; } int main() { const char a = 1; const short b = 2; const int c = 4; const long long d = 8; char da = 0; short db = 0; int dc = 0; long long dd = 0; doubleIt(a, b, c, d, &da, &db, &dc, &dd); std::cout << "double of " << +a << " is " << +da << std::endl << "double of " << b << " is " << db << std::endl << "double of " << c << " is " << dc << std::endl << "double of " << d << " is " << dd << std::endl; }
Entretanto, este não é o caso.
.model flat,c .code doubleIt_ proc push ebp mov ebp, esp ; da mov al, [ebp + 8] add al, al mov ebx,[ebp + 28] mov [ebx], al ; db mov ax, [ebp + 12] add ax, ax mov ebx,[ebp + 32] mov [ebx], ax ; dc mov eax, [ebp + 16] add eax, eax mov ebx,[ebp + 36] mov [ebx], eax ; dd mov eax, [ebp + 20] mov edx, [ebp + 24] add eax, eax adc edx, edx mov ebx,[ebp + 40] mov [ebx], eax mov [ebx+4], edx pop ebp ret doubleIt_ endp end
Em arquiteturas de 32 bits, o transporte de dados com tamanhos múltiplos desse tamanho é mais eficiente. Por essa razão, os valores na stack acabam organizados em múltiplos de 32 bits.
Dessa forma, tipos que ocupam menos que 32 bits são complementados com “espaço inútil”.
Não adianta escolher tipos menores para “economizar espaço” na stack.
Ok, Não há memory leak, mas e performance? Realizar processamentos com tipos menores é mais barato? Acessar, ler e transportar valores de tipos menores trazem aumento de velocidade no processamento contrar tipos maiores?
Para tipos primitivos, na stack, a princípio não há ganho nenhum percebido para tipos “menores”. O transporte geralmente acontece em 32 bits mesmo.