Skip to content

C++ Modifier Types

C++ provides modifiers to alter the range or precision of fundamental data types and qualifiers to provide additional information about variables, such as whether they can be modified.

Modifiers precede the base data type (like int, char, double) to tailor it for specific needs.

The main type modifiers are:

  • signed: Specifies that the type can hold positive, negative, and zero values. This is usually the default for integral types (int, short, long, long long) but can be stated explicitly.
  • unsigned: Specifies that the type can only hold non-negative values (zero and positive). This effectively doubles the maximum positive value compared to its signed counterpart, as the sign bit is used for magnitude.
  • short: Typically reduces the storage size of int, resulting in a smaller range of values. Guarantees at least 16 bits.
  • long: Typically increases the storage size of int or double. long int guarantees at least 32 bits.
  • long long: (Introduced in C++11) Guarantees at least 64 bits for integers, providing a wider range than long int.

Applicability:

  • signed and unsigned can apply to char, short int, int, long int, long long int.
  • short and long apply to int.
  • long can also apply to double (resulting in long double, typically providing higher precision).
  • long long applies to int.

C++ allows shorthand notations. For example, unsigned implies unsigned int, short implies short int, long implies long int, and long long implies long long int.

unsigned short us; // unsigned short int
long li; // long int
unsigned long long ull; // unsigned long long int

The interpretation of the bit pattern differs between signed and unsigned types, especially for values near the range limits.

#include <iostream>
int main() {
short int i; // Signed short (typically 16 bits, range -32768 to 32767)
unsigned short int j; // Unsigned short (typically 16 bits, range 0 to 65535)
j = 50000;
// Assigning a large unsigned value to a signed variable
// can lead to implementation-defined behavior or wrap-around.
i = j;
std::cout << "Signed short i: " << i << std::endl;
std::cout << "Unsigned short j: " << j << std::endl;
return 0;
}

Possible output (depends on compiler and system architecture):

Signed short i: -15536
Unsigned short j: 50000

Explanation: The bit pattern representing 50000 in an unsigned short (binary 11000011 01010000 on a 16-bit system) is interpreted as a negative number (-15536) when read as a signed short (due to two’s complement representation). Avoid such implicit conversions between signed and unsigned types if the value might be out of range for the destination type.

Type qualifiers provide additional information about a variable, primarily concerning its mutability and how its value might change.

QualifierMeaning
constDeclares an object whose value cannot be changed after initialization. Essential for writing safer, more expressive code (const-correctness). Helps the compiler optimize.
volatileIndicates that a variable’s value might be changed by means not detectable by the compiler (e.g., hardware register updated by external event, variable shared between threads without proper synchronization). It tells the compiler not to optimize away reads/writes to this variable.
constexpr (C++11)Specifies that a variable or function can be evaluated at compile time. Used for defining true compile-time constants and functions.

Notes:

  • const should be used extensively to prevent unintended modifications.
  • volatile is used less frequently in modern C++, especially with the advent of std::atomic for thread synchronization. Its main use remains for memory-mapped hardware access.
  • The restrict keyword mentioned in older C standards is not part of standard C++, although some compilers might support it as an extension. Its purpose (related to pointer aliasing) is handled differently or implicitly in C++ optimization.
  • constexpr is a powerful feature for compile-time computation and defining constants.
#include <iostream>
int main() {
const double PI = 3.14159; // Cannot change PI later
// PI = 3.14; // Error: assignment of read-only variable 'PI'
volatile int hardwareStatusRegister = 0; // Value might change unexpectedly
constexpr int compileTimeValue = 2 * 10; // Evaluated at compile time
std::cout << "PI: " << PI << std::endl;
std::cout << "Initial Hardware Status: " << hardwareStatusRegister << std::endl;
std::cout << "Compile-time value: " << compileTimeValue << std::endl;
return 0;
}