C++ Module 2 – Types, values and expressions
Integer overflow and signed/unsigned conversions
Why unsigned arithmetic wraps but signed overflow is undefined in C++, why -1 < v.size() is false, and how to catch overflow with UBSan and checks.
What you will learn
- Predict when integer arithmetic overflows and what then happens
- Avoid signed/unsigned comparison bugs with size()
- Detect overflow with UBSan and with checked arithmetic
Before you start
What you’ll learn · Before you start
What you will learn
- Predict when integer arithmetic overflows and what then happens
- Avoid signed/unsigned comparison bugs with size()
- Detect overflow with UBSan and with checked arithmetic
Before you start
On this page
Integers in C++ have a fixed number of bits, so a calculation can produce a result that does not fit. What happens
then depends on the type. Unsigned arithmetic wraps round modulo 2ᴺ: 0 − 1 is the largest value, and the largest
value + 1 is 0. Signed overflow is undefined behaviour: the compiler may assume it never happens, so code that
relies on wrapping can be compiled into something else entirely. And when signed and unsigned values meet in one
expression, the signed one is usually converted to unsigned, which is why -1 < v.size() is false. This lesson
reproduces each case with Apple clang 21 and GCC 16, catches overflow at run time with UBSan and at compile time with
constexpr, and gives the repairs: widen before you multiply, compare with std::cmp_less or std::ssize, and use
checked or saturating arithmetic where a value can really grow too large.
Unsigned arithmetic wraps round
Unsigned wraps round; int has a seam you must not cross
Text description of the diagram
Two circles show the values of a 32-bit integer type going round like a dial, growing clockwise.
- Left, unsigned: 0 is at the top and 2³¹ at the bottom. The largest value, labelled max (2³² − 1), sits just before the top. A solid arrow crosses the top, marked "wraps": max + 1 is 0, and that is defined.
- Right, int: 0 is at the top. At the bottom the largest int, INT_MAX, meets the smallest, INT_MIN. A dashed arrow across that seam is marked "undefined": INT_MAX + 1 is undefined behaviour, not INT_MIN.
An unsigned type of N bits holds 0 to 2ᴺ − 1, and the standard defines its arithmetic modulo 2ᴺ: every result is reduced into that range. Think of a dial. Going down from 0 lands on the largest value, and going up from the largest value lands on 0.
#include <cstdint>
#include <limits>
#include <print>
int main() {
unsigned stock = 0;
--stock; // below 0: wraps round to the largest value
std::println("0 - 1 as unsigned: {}", stock);
unsigned top = std::numeric_limits<unsigned>::max();
std::println("max + 1 as unsigned: {}", top + 1u);
std::uint8_t level = 250;
level += 10; // 260 is stored modulo 256
std::println("250 + 10 in std::uint8_t: {}", level);
} from session import run
run("""
clang++ -std=c++23 -Wall -Wextra wrap.cpp -o wrap
./wrap
g++-16 -std=c++23 -Wall -Wextra wrap.cpp -o wrap
./wrap
""") Output
$ clang++ -std=c++23 -Wall -Wextra wrap.cpp -o wrap $ ./wrap 0 - 1 as unsigned: 4294967295 max + 1 as unsigned: 0 250 + 10 in std::uint8_t: 4 $ g++-16 -std=c++23 -Wall -Wextra wrap.cpp -o wrap $ ./wrap 0 - 1 as unsigned: 4294967295 max + 1 as unsigned: 0 250 + 10 in std::uint8_t: 4
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 wrap_session.py
Lines starting with $ are the commands typed; everything between them is the output of the command above. Both
compilers print the same three lines, without a warning, because nothing here is an error in the language’s eyes.
The last line has an extra step. Arithmetic on types narrower than int happens in int: level += 10 computes
250 + 10 = 260 as an int, then converts 260 back to std::uint8_t, keeping it modulo 256, which gives 4. Defined
does not mean intended: a stock count that wraps to 4,294,967,295 is still a bug, only a predictable one.
Signed overflow is undefined behaviour
For signed types the standard makes no such promise. Since C++20, two’s complement is the only representation it
allows, yet the note that P1236R1 added says it plainly: overflow for signed arithmetic is undefined behaviour. This
program squares a side of 50,000, whose square, 2,500,000,000, is larger than the largest int, 2,147,483,647:
#include <print>
int main() {
int side = 50'000;
long long wrong = side * side; // int * int overflows before the result is widened
long long right = 1LL * side * side; // long long * int, then * int: no overflow
std::println("wrong: {}", wrong);
std::println("right: {}", right);
} from session import run
run("""
clang++ -std=c++23 -Wall -Wextra -fsanitize=undefined area.cpp -o area
./area
g++-16 -std=c++23 -Wall -Wextra -fsanitize=undefined area.cpp -o area
./area
""") Output
$ clang++ -std=c++23 -Wall -Wextra -fsanitize=undefined area.cpp -o area $ ./area area.cpp:5:28: runtime error: signed integer overflow: 50000 * 50000 cannot be represented in type 'int' SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior area.cpp:5:28 wrong: -1794967296 right: 2500000000 $ g++-16 -std=c++23 -Wall -Wextra -fsanitize=undefined area.cpp -o area $ ./area area.cpp:5:28: runtime error: signed integer overflow: 50000 * 50000 cannot be represented in type 'int' wrong: -1794967296 right: 2500000000
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 area_session.py
-fsanitize=undefined builds the program with UndefinedBehaviorSanitizer (UBSan), which checks operations such
as signed arithmetic while the program runs and reports the first one that goes wrong, with its file, line and
column. Both compilers’ UBSan point at line 5. The value printed for wrong, -1,794,967,296, is what the processor’s
wrapping instruction happened to produce, but nothing obliges the compiler to use that instruction or to keep the
result.
The bug is the order of events. side * side multiplies two ints, so the product is an int and overflows; only
then is it converted to long long, too late. 1LL * side * side makes the first operand a long long, so each
multiplication is done in 64 bits. The order matters: side * side * 1LL still overflows, because the
multiplications run from left to right and the first one is still int * int.
The compiler assumes overflow never happens
Undefined behaviour is not “the program crashes” or “the value wraps”. It is permission for the compiler to assume
that the situation cannot arise, and optimisers use that permission. This function tries to detect overflow by
checking whether x + 1 became smaller than x:
#include <climits>
#include <print>
// Meant to detect overflow, but it relies on x + 1 wrapping round, which a signed int never has to do.
bool will_overflow(int x) {
return x + 1 < x;
}
int main() {
std::println("will_overflow(INT_MAX): {}", will_overflow(INT_MAX));
} from session import run
run("""
clang++ -std=c++23 -Wall -Wextra overflow_check.cpp -o check
./check
clang++ -std=c++23 -Wall -Wextra -O2 overflow_check.cpp -o check
./check
g++-16 -std=c++23 -Wall -Wextra overflow_check.cpp -o check
./check
g++-16 -std=c++23 -Wall -Wextra -fsanitize=undefined overflow_check.cpp -o check
./check
""") Output
$ clang++ -std=c++23 -Wall -Wextra overflow_check.cpp -o check $ ./check will_overflow(INT_MAX): true $ clang++ -std=c++23 -Wall -Wextra -O2 overflow_check.cpp -o check $ ./check will_overflow(INT_MAX): false $ g++-16 -std=c++23 -Wall -Wextra overflow_check.cpp -o check $ ./check will_overflow(INT_MAX): false $ g++-16 -std=c++23 -Wall -Wextra -fsanitize=undefined overflow_check.cpp -o check $ ./check overflow_check.cpp:6:14: runtime error: signed integer overflow: 2147483647 + 1 cannot be represented in type 'int' will_overflow(INT_MAX): true
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 overflow_check_session.py
Apple clang without optimisation computes x + 1 with a wrapping instruction and says true. With -O2 it says
false: since x + 1 cannot overflow in a valid program, x + 1 < x is always false, and the compiler replaced the
whole test with false. GCC simplifies the comparison the same way even without optimisation. Under UBSan the
addition really happens, so the report appears and the answer is true again. A check that depends on overflow
cannot detect overflow. Compare against the limit before you add instead: x == INT_MAX, or in general
x > INT_MAX - y for a positive y.
Overflow in a constant expression is an error
When a calculation runs at compile time, in a constexpr variable or a static_assert, the compiler is required to
reject undefined behaviour instead of assuming it away:
#include <climits>
constexpr int next(int x) {
return x + 1;
}
constexpr int fine = next(41); // evaluated while compiling: 42
constexpr int broken = next(INT_MAX); // overflow while compiling: an error
int main() {
return fine + broken;
} from session import run
run("""
clang++ -std=c++23 -Wall -Wextra -c constexpr_overflow.cpp
g++-16 -std=c++23 -Wall -Wextra -c constexpr_overflow.cpp
""") Output
$ clang++ -std=c++23 -Wall -Wextra -c constexpr_overflow.cpp
constexpr_overflow.cpp:8:15: error: constexpr variable 'broken' must be initialized by a constant expression
8 | constexpr int broken = next(INT_MAX); // overflow while compiling: an error
| ^ ~~~~~~~~~~~~~
constexpr_overflow.cpp:4:14: note: value 2147483648 is outside the range of representable values of type 'int'
4 | return x + 1;
| ^
constexpr_overflow.cpp:8:24: note: in call to 'next(2147483647)'
8 | constexpr int broken = next(INT_MAX); // overflow while compiling: an error
| ^~~~~~~~~~~~~
1 error generated.
[exit status 1]
$ g++-16 -std=c++23 -Wall -Wextra -c constexpr_overflow.cpp
constexpr_overflow.cpp:8:28: in 'constexpr' expansion of 'next(2147483647)'
8 | constexpr int broken = next(INT_MAX); // overflow while compiling: an error
| ~~~~^~~~~~~~~
constexpr_overflow.cpp:4:14: error: overflow in constant expression [-fpermissive]
4 | return x + 1;
| ~~^~~
[exit status 1]
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 constexpr_session.py
fine compiles, and broken stops both compilers with the overflowing value spelled out. That makes constexpr
evaluation a cheap way to test arithmetic helpers: a static_assert on an edge case, such as the largest input, runs
every time the file compiles.
Mixing signed and unsigned
std::vector, std::string and the other standard containers report their size as std::size_t, an unsigned type.
When an expression combines it with an int, the usual arithmetic conversions turn the int into the unsigned
type, because unsigned wins when the unsigned type is at least as wide. A negative number does not survive that:
#include <iterator>
#include <print>
#include <utility>
#include <vector>
int main() {
std::vector<int> scores{70, 85, 92};
int wanted = -1; // "no score": a common sentinel
std::println("wanted < scores.size(): {}", wanted < scores.size());
std::println("std::cmp_less(wanted, size): {}", std::cmp_less(wanted, scores.size()));
std::println("wanted < std::ssize(scores): {}", wanted < std::ssize(scores));
std::println("scores.size() - 4: {}", scores.size() - 4);
} from session import run
run("""
clang++ -std=c++23 -Wall -Wextra sign_compare.cpp -o sign_compare
./sign_compare
g++-16 -std=c++23 -Wall -Wextra sign_compare.cpp -o sign_compare
./sign_compare
""") Output
$ clang++ -std=c++23 -Wall -Wextra sign_compare.cpp -o sign_compare
sign_compare.cpp:10:66: warning: comparison of integers of different signs: 'int' and 'size_type' (aka 'unsigned long') [-Wsign-compare]
10 | std::println("wanted < scores.size(): {}", wanted < scores.size());
| ~~~~~~ ^ ~~~~~~~~~~~~~
1 warning generated.
$ ./sign_compare
wanted < scores.size(): false
std::cmp_less(wanted, size): true
wanted < std::ssize(scores): true
scores.size() - 4: 18446744073709551615
$ g++-16 -std=c++23 -Wall -Wextra sign_compare.cpp -o sign_compare
sign_compare.cpp: In function 'int main()':
sign_compare.cpp:10:66: warning: comparison of integer expressions of different signedness: 'int' and 'std::vector<int>::size_type' {aka 'long unsigned int'} [-Wsign-compare]
10 | std::println("wanted < scores.size(): {}", wanted < scores.size());
| ~~~~~~~^~~~~~~~~~~~~~~
$ ./sign_compare
wanted < scores.size(): false
std::cmp_less(wanted, size): true
wanted < std::ssize(scores): true
scores.size() - 4: 18446744073709551615
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 sign_compare_session.py
wanted < scores.size() converts -1 to a std::size_t, which on these 64-bit platforms is
18,446,744,073,709,551,615, the largest value, so the comparison is false. Both compilers warn about that line with
-Wall -Wextra, and a build that treats warnings as errors catches it. The last line shows the same conversion in
reverse: scores.size() - 4 is unsigned arithmetic, so 3 − 4 wraps round to the largest value instead of giving -1.
There are three good fixes, and rule ES.100 of the C++ Core Guidelines is behind all of them: don’t mix signed and unsigned arithmetic.
std::cmp_less(a, b)from<utility>(C++20) compares two integers by their mathematical values, whatever their types. Its family includescmp_equal,cmp_greaterand the others.std::ssize(c)from<iterator>(C++20) returns a container’s size as a signed type, so the comparison is between two signed values.- Keep sizes in a signed type from the start when you do arithmetic with them, and convert once, deliberately.
Converting between integer types
Assigning a value to an integer type that cannot hold it is not overflow. Since C++20 the result is defined: the value is reduced modulo 2ᴺ for the target’s N bits, which keeps the low N bits of its two’s complement form.
#include <cstdint>
#include <print>
#include <type_traits>
int main() {
// Converting to an integer type that cannot hold the value keeps the low bits (defined since C++20).
int from_big = static_cast<int>(3'000'000'000u); // 3,000,000,000 - 2^32
std::uint8_t from_300 = static_cast<std::uint8_t>(300); // 300 - 256
unsigned from_minus_one = static_cast<unsigned>(-1); // 2^32 - 1
std::println("{} {} {}", from_big, from_300, from_minus_one);
// Arithmetic on small types happens in int: the operands are promoted first.
unsigned char a = 200, b = 100;
auto sum = a + b;
static_assert(std::is_same_v<decltype(sum), int>);
std::println("200 + 100 with unsigned char operands: {} (an int)", sum);
} from session import run
run("""
clang++ -std=c++23 -Wall -Wextra conversions.cpp -o conversions
./conversions
g++-16 -std=c++23 -Wall -Wextra conversions.cpp -o conversions
./conversions
""") Output
$ clang++ -std=c++23 -Wall -Wextra conversions.cpp -o conversions $ ./conversions -1294967296 44 4294967295 200 + 100 with unsigned char operands: 300 (an int) $ g++-16 -std=c++23 -Wall -Wextra conversions.cpp -o conversions $ ./conversions -1294967296 44 4294967295 200 + 100 with unsigned char operands: 300 (an int)
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 conversions_session.py
3,000,000,000 becomes -1,294,967,296 (3,000,000,000 − 2³²), 300 becomes 44 (300 − 256), and -1 becomes 4,294,967,295
(2³² − 1). Defined behaviour, but rarely what anyone wants, so make such conversions visible with static_cast, as
the program does. The second half shows integral promotion: before arithmetic, operands narrower than int, such
as unsigned char, short and bool, are promoted to int. That is why 200 + 100 gives 300 instead of wrapping in
8 bits, and why the type of the sum is int.
Catching and preventing overflow
Three tools cover most needs.
UBSan, while you test. -fsanitize=undefined is available in both compilers, and Clang’s documentation lists
macOS, Linux and Windows among its platforms. It reports signed overflow, and also division of INT_MIN by -1. Unsigned wrapping is not undefined, so it is not part of
-fsanitize=undefined; Clang can check it with -fsanitize=unsigned-integer-overflow when a wrap would be a bug in
your program.
Checked arithmetic, where an input can be too large. GCC and Clang provide __builtin_add_overflow,
__builtin_sub_overflow and __builtin_mul_overflow, which compute the exact result, store it if it fits and return
whether it overflowed. Wrapped in a function that returns std::optional, a caller cannot forget the check:
#include <cstdint>
#include <limits>
#include <optional>
#include <print>
#include <utility>
// The product of a and b, or nothing when it does not fit in std::int64_t.
// __builtin_mul_overflow (GCC and Clang) computes the exact product and reports whether it fitted.
std::optional<std::int64_t> safe_mul(std::int64_t a, std::int64_t b) {
std::int64_t product;
if (__builtin_mul_overflow(a, b, &product)) return std::nullopt;
return product;
}
int main() {
constexpr std::int64_t lowest = std::numeric_limits<std::int64_t>::min();
const std::pair<std::int64_t, std::int64_t> cases[] = {
{3'000'000'000, 3},
{3'037'000'499, 3'037'000'499},
{3'037'000'500, 3'037'000'500},
{lowest, 1},
{lowest, -1},
};
for (auto [a, b] : cases) {
if (auto product = safe_mul(a, b)) std::println("{} * {} = {}", a, b, *product);
else std::println("{} * {} does not fit", a, b);
}
} from session import run
run("""
clang++ -std=c++23 -Wall -Wextra checked.cpp -o checked
./checked
g++-16 -std=c++23 -Wall -Wextra checked.cpp -o checked
./checked
""") Output
$ clang++ -std=c++23 -Wall -Wextra checked.cpp -o checked $ ./checked 3000000000 * 3 = 9000000000 3037000499 * 3037000499 = 9223372030926249001 3037000500 * 3037000500 does not fit -9223372036854775808 * 1 = -9223372036854775808 -9223372036854775808 * -1 does not fit $ g++-16 -std=c++23 -Wall -Wextra checked.cpp -o checked $ ./checked 3000000000 * 3 = 9000000000 3037000499 * 3037000499 = 9223372030926249001 3037000500 * 3037000500 does not fit -9223372036854775808 * 1 = -9223372036854775808 -9223372036854775808 * -1 does not fit
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 checked_session.py
The edge cases are the instructive ones. 3,037,000,499² fits in 64 bits and 3,037,000,500² does not, because the
square root of the largest std::int64_t is about 3,037,000,499.98. And the smallest std::int64_t times -1
overflows, since the range runs to -2⁶³ but only up to 2⁶³ − 1. The same applies to -x, std::abs(x) and x / -1
when x is the smallest int, long or long long.
Saturating arithmetic, where a clamp is the right answer. Sometimes the best result of an overflow is the
nearest limit: a volume control, a pixel brightness, a counter that should stop at its maximum. C++26 adds these
functions to <numeric>. They were adopted as std::add_sat and its relatives (P0543R3), and renamed
std::saturating_add, std::saturating_sub, std::saturating_mul and std::saturating_div by P4052R0, which also
raised the feature-test macro __cpp_lib_saturation_arithmetic from 202311 to 202603 so that code can tell the
names apart. The two standard libraries here stand on either side of the rename:
#include <climits>
#include <cstdint>
#include <limits>
#include <numeric>
#include <print>
#include <version>
// Adds and stops at the type's limits instead of overflowing. C++26 calls the standard
// function std::saturating_add; the first C++26 drafts called it std::add_sat.
template <class T>
constexpr T clamp_add(T a, T b) {
#if __cpp_lib_saturation_arithmetic >= 202603L
return std::saturating_add(a, b);
#elif __cpp_lib_saturation_arithmetic >= 202311L
return std::add_sat(a, b);
#else
T sum;
if (!__builtin_add_overflow(a, b, &sum)) return sum;
return b > 0 ? std::numeric_limits<T>::max() : std::numeric_limits<T>::min();
#endif
}
int main() {
#ifdef __cpp_lib_saturation_arithmetic
std::println("__cpp_lib_saturation_arithmetic = {}", __cpp_lib_saturation_arithmetic);
#else
std::println("no saturation arithmetic in this library");
#endif
std::println("int8: 100 + 100 = {}", clamp_add<std::int8_t>(100, 100));
std::println("uint8: 250 + 10 = {}", clamp_add<std::uint8_t>(250, 10));
std::println("int: INT_MAX + 1 = {}", clamp_add(INT_MAX, 1));
std::println("int: INT_MIN + -1 = {}", clamp_add(INT_MIN, -1));
} from session import run
run("""
clang++ -std=c++26 -Wall -Wextra saturating.cpp -o saturating
./saturating
g++-16 -std=c++26 -Wall -Wextra saturating.cpp -o saturating
./saturating
g++-16 -std=c++23 -Wall -Wextra saturating.cpp -o saturating
./saturating
""") Output
$ clang++ -std=c++26 -Wall -Wextra saturating.cpp -o saturating $ ./saturating __cpp_lib_saturation_arithmetic = 202311 int8: 100 + 100 = 127 uint8: 250 + 10 = 255 int: INT_MAX + 1 = 2147483647 int: INT_MIN + -1 = -2147483648 $ g++-16 -std=c++26 -Wall -Wextra saturating.cpp -o saturating $ ./saturating __cpp_lib_saturation_arithmetic = 202603 int8: 100 + 100 = 127 uint8: 250 + 10 = 255 int: INT_MAX + 1 = 2147483647 int: INT_MIN + -1 = -2147483648 $ g++-16 -std=c++23 -Wall -Wextra saturating.cpp -o saturating $ ./saturating no saturation arithmetic in this library int8: 100 + 100 = 127 uint8: 250 + 10 = 255 int: INT_MAX + 1 = 2147483647 int: INT_MIN + -1 = -2147483648
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 saturating_session.py
With -std=c++26, Apple clang’s libc++ reports 202311 and offers only std::add_sat, while GCC 16’s libstdc++
reports 202603 and offers only std::saturating_add. The #if tests choose whichever exists, and in C++23 mode,
where neither does, the file falls back to __builtin_add_overflow and clamps by hand. All three builds print the
same results: 100 + 100 in std::int8_t stops at 127, and 250 + 10 in std::uint8_t at 255.
Interview questions
Fresher · What happens when an unsigned int overflows, and when an int does?
Unsigned arithmetic is defined modulo 2ᴺ, so it wraps: the largest unsigned int plus 1 is 0, and 0 minus 1 is the
largest value. Signed overflow, such as INT_MAX + 1, is undefined behaviour, even though C++20 requires two’s
complement. The compiler may assume it never happens: it can fold a check such as x + 1 < x to false, and the
program may print a wrapped value, a different value or something else. Converting an out-of-range value to a signed
type is different: since C++20 that is defined and keeps the low bits. Catch overflow with
-fsanitize=undefined while testing, and prevent it with wider types or checked arithmetic.
Check yourself
6 questions about this lesson. Every answer and why it is right is on the page, behind “Show the answer”. Your score stays in this browser.
Key takeaways
- Unsigned arithmetic wraps modulo 2ᴺ: defined, but often still a bug.
- Signed overflow is undefined behaviour. The compiler may assume it cannot happen, so a check like
x + 1 < xcan be compiled tofalse: compare against the limit before the operation instead. - Widen before the operation:
1LL * a * b, neverlong long r = a * b;. - In a constant expression, overflow is a compile error, which makes
static_asserta cheap test of edge cases. - An
intcompared with asize()becomes unsigned, so-1 < v.size()is false. Usestd::cmp_less,std::ssizeor signed sizes. - Converting to a narrower integer type keeps the low bits (defined since C++20), and arithmetic on narrow types
happens in
int. - Test with
-fsanitize=undefined; check with__builtin_mul_overflow; clamp with C++26 saturation arithmetic,std::saturating_addin GCC 16 and stillstd::add_satin Apple clang 21’s libc++.
References
- Working Draft, Programming Languages: C++ (N4950): [basic.fundamental], [conv.integral], [expr.arith.conv], [expr.const] (ISO/IEC JTC1/SC22/WG21)
- Working Draft, Programming Languages: C++ (N5050): [numeric.sat] Saturation arithmetic (ISO/IEC JTC1/SC22/WG21)
- P1236R1: Alternative Wording for P0907R4 Signed Integers are Two's Complement (ISO/IEC JTC1/SC22/WG21)
- P0543R3: Saturation arithmetic (ISO/IEC JTC1/SC22/WG21)
- P4052R0: Renaming saturation arithmetic functions (ISO/IEC JTC1/SC22/WG21)
- UndefinedBehaviorSanitizer (LLVM Project)
- Built-in Functions to Perform Arithmetic with Overflow Checking (GNU Project)
- Program Instrumentation Options (-fsanitize=undefined) (GNU Project)
- C++ Core Guidelines: ES.100, Don't mix signed and unsigned arithmetic (Standard C++ Foundation)
- C++ Core Guidelines: ES.103, Don't overflow (Standard C++ Foundation)
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress