Bài tập

Phạm vi giá trị các kiểu dữ liệu trong C/C++: Kiến thức quan trọng của lập trình viên

Huy Erick

Bạn đã bao giờ nghe về vấn đề "tràn số" trong lập trình chưa? Phạm vi giá trị các kiểu dữ liệu trong ngôn ngữ lập trình là một khái niệm quan trọng mà các...

Bạn đã bao giờ nghe về vấn đề "tràn số" trong lập trình chưa? Phạm vi giá trị các kiểu dữ liệu trong ngôn ngữ lập trình là một khái niệm quan trọng mà các lập trình viên nên hiểu rõ. Trong bài viết này, chúng ta sẽ tìm hiểu về vấn đề này và cách tránh bị tràn số trong lập trình C/C++.

Lỗi tràn số trong lập trình

Tràn số xảy ra khi một biến được gán giá trị nằm ngoài phạm vi của kiểu dữ liệu của nó. Đây là một lỗi rất phổ biến đối với những người mới học lập trình. Hãy xem hình minh họa dưới đây để hiểu rõ hơn về lỗi này.

Hiện tượng tràn số trong lập trình(Overflow)

Để giúp bạn hiểu rõ hơn về hiện tượng tràn số, hãy xem ví dụ mã nguồn C++ sau đây với kiểu dữ liệu char. Một số bạn có thể đã biết rằng char có kích thước 1 byte = 8 bits, tức là có thể biểu diễn 28 giá trị khác nhau. Chúng ta hãy chia đôi miền âm và dương. 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 cả số 0.

Ví dụ về lỗi tràn số

#include  using namespace std;  int main(){     char s = 97;     cout  (int)s  '\n';     cout  "Kiểm 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 lặp này sẽ lặp bao nhiêu lần?

char s = 97; cout  (int)s  '\n'; >>> 97 cout  s  '\n'; >>> a

Với s = 97, nếu ép kiểu về int, chương trình sẽ in ra số 97; nếu không, nó sẽ in ra chữ ‘a’. Do mã ASCII của a là 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. Bởi 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, hãy thử các dòng code này để xem chúng in ra gì:

#include  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 sẽ nhận được. Đó chính là hậu quả của việc tràn số.

-128 -127 -126

Chú ý: Hiện tượng tràn số có thể xảy ra trong tất cả các ngôn ngữ lập trình. Tuy nhiên, với các ngôn ngữ lập trình bậc cao, chúng đã có cơ chế kiểm soát nên bạn có thể không gặp phải hiện tượng này.

Nếu bạn quan tâm đến lập trình C/C++, hãy đọc thêm các bài viết hay khác tại chuyên mục lập trình C/C++ trên trang web của chúng tôi.

Phạm vi giá trị các kiểu dữ liệu trong C/C++

Để tránh bị tràn số trong lập trình, bạn cần phải nắm rõ phạm vi giá trị của 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. Tuy nhiên, trong bài viết này, chúng ta chỉ tìm hiểu về phạm vi giá trị của các kiểu dữ liệu trong C/C++.

Phạm vi giá trị các kiểu dữ liệu

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ủa 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 đó, trong bài viết này, mình chỉ tập trung trình bày về phạm vi giá trị của 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
signed 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
negative float -1.17549e-38 to -3.40282e+38 -FLT_MIN -FLT_MAX
double 2.22507e-308 to 1.79769e+308 DBL_MIN DBL_MAX
negative double -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 Macro trong bảng trên. Dưới đây là một đoạn code ví dụ:

// C++ code để thể hiện các Macro của các kiểu dữ liệu #include  #include  // for int, char macros #include  // for float, double macros  using namespace std;  int main() {     // Hiển thị phạm vi giá trị bằng Macro     cout  "char ranges from : "  CHAR_MIN  " to "  CHAR_MAX;     cout  "\n\nshort char ranges from : "  SCHAR_MIN  " to "  SCHAR_MAX;     cout  "\n\nunsigned char ranges from : "  0  " to "  UCHAR_MAX;     cout  "\n\nshort int ranges from : "  SHRT_MIN  " to "  SHRT_MAX;     cout  "\n\nunsigned short int ranges from : "  0  " to "  USHRT_MAX;     cout  "\n\nint ranges from : "  INT_MIN  " to "  INT_MAX;     cout  "\n\nunsigned int ranges from : "  0  " to "  UINT_MAX;     cout  "\n\nlong int ranges from : "  LONG_MIN  " to "  LONG_MAX;     cout  "\n\nunsigned long int ranges from : "  0  " to "  ULONG_MAX;     cout  "\n\nlong long int ranges from : "  LLONG_MIN  " to "  LLONG_MAX;     cout  "\n\nunsigned long long int ranges from : "  0  " to "  ULLONG_MAX;     cout  "\n\nfloat ranges from : "  FLT_MIN  " to "  FLT_MAX;     cout  "\n\nnegative float ranges from : "  -FLT_MIN  " to "  -FLT_MAX;     cout  "\n\ndouble ranges from : "  DBL_MIN  " to "  DBL_MAX;     cout  "\n\nnegative double ranges from : "  -DBL_MIN  " to "  +DBL_MAX;      return 0; }

Kết quả hiển thị:

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

Chúng ta sẽ lấy ví dụ với kiểu char để minh họa. Để hiểu phạm vi giá trị, chúng ta cần biết về khái niệm hoán vị.

Như các bạn đã biết, 1 bit trong lập trình có thể biểu diễn 2 giá trị: 0 hoặc 1, tức là biểu diễn được 2^1 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 2^2 giá trị.

Như vậy, 1 byte = 8 bits sẽ biểu diễn được 2^8 giá trị khác nhau.

Máy tính lưu giá trị mã bit 0 và 1, tức là 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

Qua bài viết này, bạn đã hiểu được phạm vi giá trị của các kiểu dữ liệu trong ngôn ngữ C++. Đồng thời, bạn cũng đã biết về hiện tượng tràn số và cách tránh bị tràn số. Nếu bạn còn thắc mắc, hãy để lại câu hỏi trong phần bình luận.

Chú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/

1