Trong bài học trước về số nguyên có ký hiệu, chúng ta đã tìm hiểu về các kiểu dữ liệu số nguyên có thể chứa cả số âm và số dương, bao gồm số 0. Nhưng trong ngôn ngữ lập trình C++, chúng ta cũng có hỗ trợ cho các số nguyên không dấu. Số nguyên không dấu chỉ chứa các số nguyên không âm.
Định nghĩa số nguyên không dấu
Để định nghĩa một số nguyên không dấu, chúng ta sử dụng từ khóa "unsigned" trước kiểu dữ liệu:
unsigned short us;
unsigned int ui;
unsigned long ul;
unsigned long long ull;
Phạm vi của số nguyên không dấu
Số nguyên không dấu 1 byte có phạm vi từ 0 đến 255. So sánh với số nguyên có ký hiệu, 1 byte có phạm vi từ -128 đến 127. Cả hai kiểu này có thể lưu trữ 256 giá trị khác nhau, nhưng số nguyên có ký hiệu sử dụng một nửa phạm vi cho các số âm, trong khi số nguyên không dấu có thể lưu trữ số dương lớn gấp đôi.
Dưới đây là một bảng hiển thị phạm vi cho các số nguyên không dấu:
Size/Type | Range |
---|---|
1 byte | unsigned 0 to 255 |
2 byte | unsigned 0 to 65,535 |
4 byte | unsigned 0 to 4,294,967,295 |
8 byte | unsigned 0 to 18,446,744,073,709,551,615 |
Một biến không dấu n-bit có phạm vi từ 0 đến (2n) -1.
Khi bạn không cần số âm, số nguyên không dấu rất phù hợp cho các mạng và hệ thống có ít bộ nhớ, vì số nguyên không dấu có thể lưu trữ số dương nhiều hơn mà không chiếm thêm bộ nhớ.
Các điều cần ghi nhớ về số nguyên có và không có dấu
Lập trình viên mới thường lẫn lộn giữa số nguyên có và không có dấu. Để phân biệt số âm và số dương, chúng ta sử dụng dấu âm. Nếu một số không có ký hiệu (+, -), chúng ta giả sử rằng số đó là dương. Vì vậy, số nguyên có dấu có thể có giá trị là dương và âm, trong khi số nguyên không dấu chỉ có giá trị dương.
Tràn số nguyên không dấu
Có một câu hỏi thú vị: Điều gì sẽ xảy ra nếu chúng ta cố gắng lưu trữ số 280 (yêu cầu 9 bit để biểu diễn) trong một số nguyên không dấu 1 byte? Có thể bạn nghĩ rằng nó sẽ tràn số! Nhưng thực tế không phải như vậy.
Theo định nghĩa, số nguyên không dấu không thể tràn số. Hãy xem ví dụ sử dụng số nguyên 2 byte:
#include
int main() {
unsigned int x{ 3 };
unsigned int y{ 5 };
std::cout << x - y << '\n';
return 0;
}
Chương trình này sẽ in ra kết quả là gì?
Cuộc tranh cãi về số nguyên không dấu
Nhiều lập trình viên (bao gồm cả Google) tin rằng lập trình viên nên tránh sử dụng số nguyên không dấu. Điều này chủ yếu vì hai hành vi sau có thể gây ra vấn đề.
Đầu tiên, hãy xem xét phép trừ của hai số không dấu như 3 và 5. 3 trừ 5 là -2, nhưng -2 có thể được biểu diễn dưới dạng một số không dấu như sau:
#include
int main() {
unsigned int x{ 3 };
unsigned int y{ 5 };
std::cout << x - y << '\n';
return 0;
}
Chương trình này sẽ cho ra kết quả gì?
Thứ hai, hành vi không mong muốn có thể xảy ra khi bạn trộn các số có dấu và không dấu. Trong ví dụ trên, ngay cả khi một trong các toán hạng (x hoặc y) có dấu, toán hạng khác (không dấu) sẽ làm cho toán tử có dấu được chuyển thành một số nguyên không dấu, và hành vi này sẽ dẫn đến kết quả không mong đợi.
Hãy xem xét đoạn mã sau:
void doSomething(unsigned int value) {
// Do something
}
int main() {
int x{ -1 };
doSomething(x);
return 0;
}
Tác giả của doSomething()
đã mong đợi rằng hàm này sẽ chỉ được gọi với số dương. Nhưng trong trường hợp này, người gọi truyền vào -1. Điều gì sẽ xảy ra?
Đối số có dấu -1 sẽ được chuyển đổi thành tham số không dấu. -1 không nằm trong phạm vi của một số không dấu, vì vậy nó sẽ được chuyển đổi thành một số lớn (có thể là 4294967295). Điều này dẫn đến kết quả không đúng. Tệ hơn nữa, không có cách tốt để ngăn chặn tình trạng này xảy ra. C++ tự do chuyển đổi giữa các số có dấu và không dấu.
Nếu bạn cần bảo vệ một hàm khỏi các đầu vào tiêu cực, hãy sử dụng xác nhận hoặc ngoại lệ. Cả hai cách đều bảo vệ điều này.
Một số ngôn ngữ lập trình hiện đại (như Java) và các framework như .NET không bao gồm các kiểu không dấu hoặc giới hạn việc sử dụng chúng.
Các lập trình viên mới thường sử dụng các số nguyên không dấu để biểu diễn dữ liệu không âm hoặc để tận dụng phạm vi bổ sung. Nhà thiết kế của C++, Bjarne Stroustrup, đã cho biết việc sử dụng số nguyên không dấu thay vì số có dấu để có thêm một bit để biểu diễn các số dương gần như không phải là một ý tưởng hay.
Lưu ý: Tránh sử dụng số không dấu trừ khi cụ thể yêu cầu hoặc không thể tránh khỏi. Nếu bạn sử dụng số không dấu, hãy tránh trộn lẫn các số có dấu và không dấu nếu có thể.
Vậy đâu khi nào là hợp lý để sử dụng số không dấu?
Tuy nhiên, vẫn còn một số trường hợp trong C++ nơi sử dụng số không dấu là hợp lý hoặc cần thiết.
Đầu tiên, số nguyên không dấu thích hợp khi xử lý thao tác bit.
Thứ hai, việc sử dụng số nguyên không dấu là không thể tránh khỏi trong một số trường hợp, đặc biệt là khi làm việc với việc lập chỉ mục cho mảng. Chúng ta sẽ tìm hiểu thêm về điều này trong những bài học về mảng và lập chỉ mục cho mảng.
Cũng lưu ý rằng nếu bạn đang phát triển cho một hệ thống nhúng (ví dụ: Arduino) hoặc một số bối cảnh giới hạn bộ xử lý / bộ nhớ khác, việc sử dụng số không dấu là phổ biến hơn và được chấp nhận (và trong một số trường hợp, không thể tránh khỏi) vì hiệu suất.
Nguồn: Devmaster Academy qua CafeDev