Integer Widths in C++: long, long long, and Beyond
C++ provides a range of integer types, and understanding their differences, particularly the use of long and long long, can be crucial for writing efficient and portable code. This tutorial explains these types, their origins, and how they differ across various platforms.
The Basics: int, short, and long
At the foundation, C++ has integer types like int, short, and long. These represent whole numbers, but they vary in the amount of memory they occupy, which dictates the range of values they can hold. The int type is often the ‘natural’ size for the target architecture (e.g., 32 or 64 bits). short is typically smaller than int, and long is typically at least as large as int.
Historically, the long type was introduced to provide a wider integer representation than the standard int. However, the exact size of these types isn’t fixed by the C++ standard itself; it depends on the compiler and the underlying hardware architecture.
Introducing long long
The long long type was added to the C++ standard (C++99 and later) to guarantee a wider integer representation, at least 64 bits wide. This provides a means to store very large integer values that wouldn’t fit within a standard long on some systems.
long vs. long int and long long vs. long long int
It’s important to understand that long is simply a shorthand for long int, and long long is a shorthand for long long int. The int suffix is optional and doesn’t change the type. Both forms are valid and equivalent.
Guaranteed Sizes and Portability
The C++ standard guarantees minimum sizes for integer types, but the actual size can vary depending on the platform. Here’s a breakdown of the minimum guaranteed ranges:
char: At least 8 bitsshort: At least 16 bitsint: At least 16 bitslong: At least 32 bitslong long: At least 64 bits
This means a long long will always be at least 64 bits, but a long might be 32 or 64 bits depending on the system.
Data Models and Platform Differences
The variations in long‘s size arise from different 64-bit data models. These models determine the size of pointers and long integers on 64-bit architectures:
- LP64: (Used on Linux and macOS)
longis 64 bits,intis 32 bits. Pointers are 64 bits. - LLP64: (Used on Windows)
longis 32 bits,long longis 64 bits. Pointers are 64 bits.
This means that a program that assumes a fixed size for long might behave differently on Linux and Windows.
Best Practices and Modern C++
To avoid ambiguity and ensure portability, modern C++ encourages using fixed-width integer types provided by the <cstdint> header. These types offer precise control over integer sizes:
#include <cstdint>
int main() {
std::int8_t small_value; // Exactly 8 bits
std::int16_t short_value; // Exactly 16 bits
std::int32_t int_value; // Exactly 32 bits
std::int64_t long_value; // Exactly 64 bits
std::size_t index; // Unsigned integer type suitable for array indexing
return 0;
}
These types eliminate the platform-dependent behavior of int, long, and long long, leading to more predictable and portable code. std::size_t is particularly useful for representing sizes and indices, as it’s guaranteed to be large enough to hold the size of any object.
long double
Finally, long double is a floating-point type that provides extended precision compared to double. While not an integer type, it’s often used alongside integers for calculations and is guaranteed to have at least as much precision as double. The exact precision of long double is platform-dependent.