Unsigned keyword in C++
Categories:
Understanding the 'unsigned' Keyword in C++

Explore the 'unsigned' keyword in C++, its impact on integer types, and how it affects data representation and arithmetic operations. Learn best practices for its use.
In C++, the unsigned
keyword is a type modifier that can be applied to integer data types (like int
, short
, long
, long long
, and char
). Its primary purpose is to specify that the variable can only hold non-negative values (zero and positive integers). This seemingly small change has significant implications for how numbers are stored, their maximum range, and how arithmetic operations are performed.
What 'unsigned' Means for Data Representation
When an integer type is declared as unsigned
, the most significant bit (MSB), which is typically used to represent the sign of the number (0 for positive, 1 for negative), is instead used as part of the magnitude. This effectively doubles the positive range of the integer type while eliminating the ability to store negative values. For example, a standard 32-bit int
might range from approximately -2 billion to +2 billion, while a 32-bit unsigned int
would range from 0 to approximately +4 billion.
#include <iostream>
#include <limits>
int main() {
int signed_val = -10;
unsigned int unsigned_val = 10;
std::cout << "Signed int min: " << std::numeric_limits<int>::min() << std::endl;
std::cout << "Signed int max: " << std::numeric_limits<int>::max() << std::endl;
std::cout << "Unsigned int min: " << std::numeric_limits<unsigned int>::min() << std::endl;
std::cout << "Unsigned int max: " << std::numeric_limits<unsigned int>::max() << std::endl;
// Attempting to assign a negative value to an unsigned int
unsigned int negative_attempt = -1;
std::cout << "Assigned -1 to unsigned int: " << negative_attempt << std::endl;
return 0;
}
Demonstrating the range of signed vs. unsigned integers and the effect of assigning a negative value to an unsigned type.
unsigned
integer type results in a large positive value due to integer overflow/underflow behavior. This is a common source of bugs and unexpected behavior in C++ programs.Arithmetic with Unsigned Integers
Arithmetic operations involving unsigned
integers behave differently than those with signed
integers, especially when mixing types or dealing with underflow. When an operation results in a value less than zero for an unsigned
type, it 'wraps around' to the maximum possible value for that type. This behavior is well-defined in C++ and is often referred to as modular arithmetic.
#include <iostream>
int main() {
unsigned int a = 5;
unsigned int b = 10;
unsigned int result_subtraction = a - b; // 5 - 10 = -5
std::cout << "Unsigned subtraction (5 - 10): " << result_subtraction << std::endl; // Will be a very large positive number
int signed_c = -5;
unsigned int unsigned_d = 10;
int mixed_result = signed_c + unsigned_d; // -5 + 10 = 5
std::cout << "Mixed type addition (-5 + 10): " << mixed_result << std::endl;
unsigned int mixed_result_unsigned = signed_c + unsigned_d; // -5 + 10 = 5
std::cout << "Mixed type addition to unsigned (-5 + 10): " << mixed_result_unsigned << std::endl;
// Comparison between signed and unsigned
int s_val = -1;
unsigned int u_val = 1;
if (s_val < u_val) {
std::cout << "-1 is less than 1 (as expected)" << std::endl;
} else {
std::cout << "-1 is NOT less than 1 (due to implicit conversion)" << std::endl;
}
unsigned int large_unsigned = 4294967295U; // Max for 32-bit unsigned int
unsigned int small_unsigned = 1;
unsigned int overflow_add = large_unsigned + small_unsigned;
std::cout << "Unsigned overflow (max + 1): " << overflow_add << std::endl; // Wraps around to 0
return 0;
}
Illustrating unsigned arithmetic, including underflow and mixed-type comparisons.
flowchart TD A[Start: Declare unsigned int] --> B{Operation: Subtract larger value?} B -- Yes --> C[Result: Wraps around to large positive number] B -- No --> D[Result: Standard positive value] A --> E{Operation: Compare with signed int?} E -- Yes --> F[Implicit Conversion: Signed int becomes unsigned] F --> G[Comparison: May yield unexpected results] C --> H[End] D --> H[End] G --> H[End]
Flowchart illustrating the behavior of unsigned integer arithmetic and comparisons.
When to Use 'unsigned'
The unsigned
keyword is best used when you are certain that a variable will never need to store a negative value. Common use cases include:
- Bit flags and masks: When individual bits represent distinct states or permissions.
- Counts and sizes: Variables storing the number of items, array sizes, or loop counters (e.g.,
std::vector::size_type
is typically unsigned). - Memory addresses: Pointers are often treated as unsigned integers when their raw numerical value is needed.
- Hash values: Hash functions typically produce non-negative outputs.
However, for general-purpose arithmetic where negative values are possible or where there's a risk of underflow, signed
integers are usually safer and more intuitive. Mixing signed
and unsigned
types in expressions can lead to implicit conversions that might produce unexpected results, as demonstrated in the code example above.
signed
integers for general arithmetic to avoid unexpected wrap-around behavior. Use unsigned
only when you explicitly need the extended positive range or when dealing with bitwise operations and counts that are inherently non-negative.