Phạm vi giá trị các kiểu dữ liệu trong C/C++ là một vấn đề mà rất ít bạn quan tâm. Nhưng đây lại là một kiến thức rất quan trọng mà các lập trình viên nên biết. Không biết các bạn đã nghe tới vấn đề bị “tràn số” hay chưa. Để tránh bị lỗi tràn số, các bạn cần biết phạm vi giá trị các kiểu dữ liệu trong ngôn ngữ lập trình.
Lỗi tràn số trong lập trình
Tràn số là hiện tượng biến được gán giá trị nằm ngoài khoảng giá trị thuộc phạm vi của kiểu dữ liệu của biến đó. Đây là một lỗi khá phổ biến đối với các bạn mới học lập trình. Hình ảnh dưới đây mang tính chất mình họa cho lỗi này.
Để giúp các bạn chưa biết về hiện tượng tràn số, mình sẽ lấy ví dụ code C++ với kiểu dữ liệu char. Chắc nhiều bạn đã biết char có kích thước 1 byte = 8 bits. Tức có thể biểu diễn 28 giá trị khác nhau. Ta lại chia đôi 2 miền âm dương ra. Tức là từ -27 đến 27 – 1. Bởi vì phía bên nửa dương cần biểu diễn cho cả số 0 nữa.
Ví dụ về lỗi tràn số
#include <iostream> using namespace std; int main(){ char s = 97; cout << (int)s << 'n'; cout << "Kiem tra char overflow c++n"; for(char i = 127; i <= 150; ++i){ cout << (int)i << 'n'; } }
Các bạn nghĩ chương trình này sẽ in ra gì? Vòng for kia sẽ lặp bao nhiêu lần?
char s = 97; cout << (int)s << 'n'; >>> 97 cout << s << 'n'; >>> a
Với s = 97, mình sẽ in ra được số 97 nếu ép kiểu về int, nếu không sẽ in ra chữ ‘a’. Do mã ASCII của a = 97.
Quay lại với câu hỏi trên. Đáp án là vòng lặp này sẽ lặp vô tận. Vì giá trị lớn nhất của char là 127. Khi nó nhận giá trị từ 128 trở lên, sẽ xảy ra hiện tượng tràn số.
Nếu không tin, bạn hãy chạy thử. Hoặc đơn giản hơn, thử mấy dòng code này coi nó in ra gì:
#include <iostream> using namespace std; int main(){ char s = 128; cout << (int)s << 'n'; s = 129; cout << (int)s << 'n'; s = 130; cout << (int)s << 'n'; }
Đây là output bạn nhận được. Đó là hậu quả do tràn số gây ra.
-128 -127 -126
Chú ý: Hiện tượng tràn số có thể xảy ra ở tất cả các ngôn ngữ lập trình. Với các ngôn ngữ lập trình bậc cao, bản thân nó đã có cơ chế kiểm soát nên có thể bạn không thấy.
Nếu bạn đang quan tâm tới lập trình C/C++, hãy đọc các bài viết hay khác tại chuyên mục lập trình C/C++.
Phạm vi giá trị các kiểu dữ liệu trong C/C++
Vậy làm gì để tránh bị tràn số trong lập trình nói chung? Bạn cần phải nắm được phạm vi giá trị các kiểu dữ liệu trong mỗi ngôn ngữ lập trình. Về cơ bản nhiều kiểu dữ liệu trong các ngôn ngữ lập trình là giống nhau. Do vậy, trong bài viết này, mình chỉ trình bày về phạm vi giá trị các kiểu dữ liệu trong C/C++.
Bảng phạm vi giá trị các kiểu dữ liệu trong C/C++
Data Type Range Macro for min value Macro for max value char -128 to +127 CHAR_MIN CHAR_MAX short char -128 to +127 SCHAR_MIN SCHAR_MAX unsigned char 0 to 255 0 UCHAR_MAX short int -32768 to +32767 SHRT_MIN SHRT_MAX unsigned short int 0 to 65535 0 USHRT_MAX int -2147483648 to +2147483647 INT_MIN INT_MAX unsigned int 0 to 4294967295 0 UINT_MAX long int -9223372036854775808 to +9223372036854775807 LONG_MIN LONG_MAX unsigned long int 0 to 18446744073709551615 0 ULONG_MAX long long int -9223372036854775808 to +9223372036854775807 LLONG_MIN LLONG_MAX unsigned long long int 0 to 18446744073709551615 0 ULLONG_MAX float 1.17549e-38 to 3.40282e+38 FLT_MIN FLT_MAX float(negative) -1.17549e-38 to -3.40282e+38 -FLT_MIN -FLT_MAX double 2.22507e-308 to 1.79769e+308 DBL_MIN DBL_MAX double(negative) -2.22507e-308 to -1.79769e+308 -DBL_MIN -DBL_MAX
Để xem phạm vi của các kiểu dữ liệu này, bạn có thể sử dụng các Marco ở bảng trên. Dưới đây là code ví dụ:
// C++ code to demonstrate the macros for data types #include<iostream> #include<limits.h> // for int,char macros #include<float.h> // for float,double macros using namespace std; int main() { // Displaying ranges with the help of macros cout << "char ranges from : " << CHAR_MIN << " to " << CHAR_MAX; cout << "nnshort char ranges from : " << SCHAR_MIN << " to " << SCHAR_MAX; cout << "nnunsigned char ranges from : " << 0 << " to " << UCHAR_MAX; cout << "nnnshort int ranges from : " << SHRT_MIN << " to " << SHRT_MAX; cout << "nnunsigned short int ranges from : " << 0 << " to " << USHRT_MAX; cout << "nnint ranges from : " << INT_MIN << " to " << INT_MAX; cout << "nnunsigned int ranges from : " << 0 << " to " << UINT_MAX; cout << "nnlong int ranges from : " << LONG_MIN << " to " << LONG_MAX; cout << "nnunsigned long int ranges from : " << 0 << " to " << ULONG_MAX; cout << "nnlong long int ranges from : " << LLONG_MIN << " to " << LLONG_MAX; cout << "nnunsigned long long int ranges from : " << 0 << " to " << ULLONG_MAX; cout << "nnnfloat ranges from : " << FLT_MIN << " to " << FLT_MAX; cout << "nnnegative float ranges from : " << -FLT_MIN << " to " << -FLT_MAX; cout << "nndouble ranges from : " << DBL_MIN << " to " << DBL_MAX; cout << "nnnegative double ranges from : " << -DBL_MIN << " to " << +DBL_MAX; return 0; }
Output:
char ranges from : -128 to 127 short char ranges from : -128 to 127 unsigned char ranges from : 0 to 255 short int ranges from : -32768 to 32767 unsigned short int ranges from : 0 to 65535 int ranges from : -2147483648 to 2147483647 unsigned int ranges from : 0 to 4294967295 long int ranges from : -9223372036854775808 to 9223372036854775807 unsigned long int ranges from : 0 to 18446744073709551615 long long int ranges from : -9223372036854775808 to 9223372036854775807 unsigned long long int ranges from : 0 to 18446744073709551615 float ranges from : 1.17549e-38 to 3.40282e+38 negative float ranges from : -1.17549e-38 to -3.40282e+38 double ranges from : 2.22507e-308 to 1.79769e+308 negative double ranges from : -2.22507e-308 to 1.79769e+308
Giải thích phạm vi của kiểu dữ liệu
Mình sẽ lấy ví dụ với kiểu char cho nhỏ nhé. Mình cần các bạn biết về hoán vị nữa.
Như các bạn biết, 1 bit trong lập trình sẽ biểu diễn được 2 giá trị: 0 hoặc 1. Tức là biểu diễn được 21 giá trị.
Vậy 2 bits sẽ biểu diễn được 4 giá trị: 00, 01, 10 và 11. Tức là biểu diễn được 22 giá trị.
Như vậy, 1 byte = 8 bits sẽ biểu diễn được 28 giá trị khác nhau.
Mà máy tính lưu giá trị mã bit 0 và 1. Tức dạng nhị phân. Do đó 00 = 0, 01 = 1, 10 = 2, 11 = 3 trong cơ số 10. Như vậy, giá trị lớn nhất mà 1 byte có thể biểu diễn là 11111111 = 2^7 + 2^6 + 2^5 + 2^4 + 2^3 + 2^2 + 2^1 + 2^0 = 255.
Đó là lý do kiểu unsigned char có phạm vi giá trị từ 0 đến 255. Còn kiểu char do có cả miền âm nên sẽ bị chia đôi(-128 đến 127). Phần dương bị hụt 1 giá trị do cần biểu diễn cả số 0.
Kết luận
Như vậy, trong bài viết này Nguyễn Văn Hiếu đã giúp các bạn biết được phạm vi giá trị các kiểu dữ liệu trong ngôn ngữ C++. Đồng thời, các bạn cũng đã biết về hiện tượng tràn số và cách để tránh bị tràn số. Còn nếu bạn nào chưa biết cách thì để lại câu hỏi ở bình luận nhé.
Chúc các bạn học tập tốt!
Tài liệu tham khảo
[1]. https://www.geeksforgeeks.org/data-type-ranges-and-their-macros-in-c/
Để lại một bình luận