Bỏ qua để đến nội dung

Tràn số nguyên (Integer Overflow)

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;
}
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
}

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”.

#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ơn
long 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
}

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ố unsigned luô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 unsigned cho 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