Tràn số nguyên (Integer Overflow)
Vấn đề
Phần tiêu đề “Vấn đề”Mỗi kiểu số nguyên có một giới hạn giá trị cố định. Khi phép tính vượt quá giới hạn đó, C++ không báo lỗi - giá trị sẽ “quay vòng” một cách âm thầm:
#include <iostream>#include <limits>
int main() { int max_int = std::numeric_limits<int>::max(); std::cout << max_int << std::endl; // 2147483647 std::cout << max_int + 1 << std::endl; // -2147483648 (!) - quay vòng về số ÂM return 0;}Nguy hiểm hơn với unsigned
Phần tiêu đề “Nguy hiểm hơn với unsigned”unsigned int u = 0;u = u - 1; // Không có số âm ở unsigned! Quay vòng thành số RẤT LỚN
std::cout << u << std::endl; // 4294967295 (giá trị lớn nhất của unsigned int)Đây là lỗi cực kỳ nguy hiểm khi dùng unsigned để đếm số lượng - một phép trừ tưởng chừng vô hại có thể tạo ra một số khổng lồ, dẫn đến vòng lặp chạy gần như vô hạn hoặc truy cập mảng ngoài phạm vi.
std::vector<int> numbers = {1, 2, 3};for (unsigned int i = numbers.size() - 1; i >= 0; i--) { // NGUY HIỂM nếu size() là 0! // Nếu numbers rỗng: 0 - 1 tràn thành số rất lớn, i >= 0 LUÔN đúng -> vòng lặp gần như vô hạn}Vì sao không báo lỗi?
Phần tiêu đề “Vì sao không báo lỗi?”Tràn số nguyên có dấu (signed integer overflow) là undefined behavior theo chuẩn C++ - trình biên dịch có quyền giả định nó không bao giờ xảy ra, và có thể tối ưu hóa code dựa trên giả định đó, đôi khi dẫn đến hành vi khó lường hơn cả việc “chỉ đơn giản quay vòng”.
Giải pháp
Phần tiêu đề “Giải pháp”#include <iostream>#include <limits>
int a = 2000000000;int b = 2000000000;
// Kiểm tra trước khi cộng để tránh tràn sốif (a > std::numeric_limits<int>::max() - b) { std::cout << "Se bi tran so!" << std::endl;} else { std::cout << a + b << std::endl;}
// Hoặc đơn giản là dùng kiểu dữ liệu đủ lớn hơnlong long safe_sum = (long long)a + b;std::cout << safe_sum << std::endl; // 4000000000 - đúng!Với vòng lặp giảm dần, tránh dùng unsigned khi chỉ số có thể về âm - dùng int/size_t cẩn thận với điều kiện dừng rõ ràng:
for (int i = static_cast<int>(numbers.size()) - 1; i >= 0; i--) { // An toàn - i là số có dấu, có thể xuống dưới 0 để vòng lặp dừng đúng}Tóm tắt
Phần tiêu đề “Tóm tắt”Tràn số nguyên: phép tính vượt quá giới hạn kiểu dữ liệu sẽ “quay vòng” âm thầm, không báo lỗi.
- Tràn số có dấu là undefined behavior; tràn số
unsignedluôn quay vòng theo modulo (dự đoán được nhưng vẫn dễ gây lỗi logic)- Đặc biệt nguy hiểm khi dùng
unsignedcho biến đếm có thể giảm về âm- Kiểm tra trước khi tính toán, hoặc dùng kiểu dữ liệu đủ lớn (
long long) khi cần