Chủ Nhật, 29 tháng 11, 2020

Lập trình C++ Basic | #8. Nhập Xuất trong C++

 

 Chương 2: CÁC KHÁI NIỆM CƠ BẢN TRONG C++

Do your c, c plus plus or c sharp assignments by Rajarjs

Xuất dữ liệu với std::cout trong C++

Đối tượng std::cout là một đối tượng được định nghĩa trong iostream library thuộc namespace std, dùng để hiển thị một thông tin nào đó lên thiết bị xuất chuẩn (mặc định là màn hình). Toán tử << (insertion operator) được dùng chung với std::cout, cho biết hướng đi của data từ r-value đến màn hình console.

Trong mỗi bài học trước, đều có những ví dụ liên quan đến việc sử dụng đối tượng std::cout để xuất một thông tin nào đó ra màn hình console. Một ví dụ kinh điển về chương trình mà bất cứ một developer nào cũng từng viết mỗi khi học một ngôn ngữ mới:

#include <iostream> using namespace std; int main() { cout << "Hello Duy Tan University"; // in lên màn hình dòng chữ "Hello HowKteam.com" return 0; }
1
2
3
4
5
6
7
8

Bạn có thể sử dụng toán tử << (insertion operator) nhiều lần để in nhiều thông tin trên cùng một dòng. Ví dụ:

#include <iostream> using namespace std; int main() { cout << "Hello Duy Tan University" << endl; // sử dụng 2 lần toán tử << int n1{ 60 }; // n1 = 60 int n2{ 9 }; // n2 = 9 // in chuỗi "Sum: 60 + 9 = 69" và xuống dòng cout << "Sum: " << n1 << " + " << n2 << n1 + n2 << "\n"; return 0; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14

Newline ‘\n’ và std::endl

Đến đây, có lẽ sẽ có một số bạn vẫn thắc mắc về sự khác nhau giữa đối tượng std::endl và escape sequence ‘\n’.

Nếu bạn viết một chương trình như bên dưới và sử dụng cả 2 cách, bạn sẽ có được kết quả như nhau:

std::cout << "Hello Duy Tan University" << std::endl; std::cout << "PNU CS211\n";
1
2

Tuy nhiên, 2 cách này có thực sự giống nhau? Câu trả lời là khôngbản chất của std::endl được thể hiện ở 2 câu lệnh bên dưới:

std::cout << "Hello Duy Tan University" << std::endl; // Tương đương với: std::cout << "Hello Duy Tan University\n" << std::flush;
1
2
3
4
5

Trong C++, output stream thường dùng buffer, nghĩa là output data sẽ được lưu vào một vùng nhớ đệm, và output data sẽ được gửi đến output device vào thời điểm thích hợp (vì lý do hiệu suất). Với std::endl sẽ xóa output buffer mỗi khi nó được gọi, trong khi ‘\n’ thì không.

Vậy, khi nào nên sử dụng std::endl và \n’:

  • Nên sử dụng std::endl khi bạn cần đảm bảo output của bạn có ngay lập tức (Vd: khi viết một record vào một file, hoặc khi update một thanh tiến trình). Nhưng nên hạn chế sử dụng std::endl khi làm việc với file I/O để tránh việc phải flush buffer liên tục dẫn đến việc phải truy cập các file I/O thường xuyên (giảm hiệu suất).
  • Ngoài ra, những trường hợp khác nên sử dụng ‘\n’.

Nhập dữ liệu với std::cin trong C++

Đối tượng std::cin là một đối tượng được định nghĩa trong iostream library thuộc namespace std, dùng để đọc một thông tin nào đó từ thiết bị nhập chuẩn (mặc định là bàn phím), sau đó lưu thông tin đó vào một biến. Toán tử >> (extraction operator) được dùng chung với std::cin, cho biết hướng đi của data từ màn hình console vào một biến.

Bên dưới là một chương trình yêu cầu người dùng nhập một số, sau đó xuất số vừa nhập ra màn hình:

#include <iostream> using namespace std; int main() { int n{ 0 }; // thông báo yêu cầu user nhập tuổi cout << "Enter your age: "; // đọc giá trị từ console và lưu vào biến n cin >> n; // in giá trị biến n (tuổi) lên màn hình cout << "Your age: " << n << endl; return 0; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

Outputs:

Nhập, Xuất và Định dạng dữ liệu trong C++ (Input and Output) 

Ở chương trình trên, nếu bạn nhập vào một số thực, khi số đó được lưu vào biến n thì C++ sẽ ép kiểu ngầm định số thực vừa nhập về số nguyên, tức là phần thập phân sẽ bị mất đi.

Ví dụ:

Nhập, Xuất và Định dạng dữ liệu trong C++ (Input and Output)

Nếu bạn đang cố gắng nhập một giá trị bất kỳ không phải là sốhoặc một số nằm ngoài phạm vi kiểu dữ liệu của biến đó, thì giá trị đó sẽ không được gán cho biến. Lúc này, giá trị biến sẽ không thay đổi.

Ví dụ:

 Nhập, Xuất và Định dạng dữ liệu trong C++ (Input and Output)


Nhập nhiều giá trị liên tiếp trong một câu lệnh

Giống như std::cout, bạn cũng có thể nhập giá trị cho nhiều biến bằng cách sử dụng nhiều toán tử >> (extraction operator) trong một câu lệnh. Đối tượng std::cin sẽ lấy mỗi giá trị theo ký tự khoảng trắng, hoặc ký tự xuống dòng từ trái qua phải và từ trên xuống dưới.

Ví dụ:

#include <iostream> #include <iomanip> // for std::setprecision() using namespace std; int main() { int nDay{ 0 }; int nMonth{ 0 }; int nYear{ 0 }; // thông báo yêu cầu user nhập ngày, tháng, năm sinh cout << "Enter your birthday: "; // đọc giá trị từ console và lưu vào biến nDay, nMonth, nYear cin >> nDay >> nMonth >> nYear; // in 3 giá trị vừa nhập lên màn hình cout << "Your birthday: " << nDay << "/" << nMonth << "/" << nYear << endl; return 0; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

Outputs:

Nhập, Xuất và Định dạng dữ liệu trong C++ (Input and Output)

Trong chương trình trên, 6 và 9 cách nhau bởi ký tự khoảng trắng, 1969 nằm ở một dòng mới. Đối tượng std::cin đã lưu được 3 giá trị vào mỗi biến tương ứng.


Định dạng dữ liệu nhập xuất trong C++

Trong C++, bạn có thể định dạng dữ liệu nhập xuất cho thiết bị nhập xuất chuẩn (bàn phím, màn hình console), hoặc có thể định dạng dữ liệu nhập xuất cho file văn bản.

Để định dạng dữ liệu, bạn cần thêm chỉ thị #include <iomanip> vào đầu chương trình. Thư viện này chứa các toán tử định dạng (manipulator).

Ví dụ: std::endl cũng là một manipulator thuộc <iostream> library. Bên dưới là những manipulator khá thông dụng trong C++:

  • Toán tử std::setw(n): xác định độ rộng dành cho của dữ liệu xuất. Khi sử dụng std::setw(n)các khoảng trắng sẽ được thêm vào bên trái hoặc bên phải dữ liệu xuất ( để tổng số ký tự là n). Dữ liệu khi in ra sẽ được canh trái hoặc canh phải.
  • Toán tử std::left và std::right dùng chung với std::setw(n) để canh lề trái hoặc lề phải.
  • Toán tử std::setfill(ch) dùng chung với std::setw(n) để quy định ký tự ch được thêm vào thay vì dùng khoảng trắng mặc định. Ví dụ: nếu dùng std::setfill(‘-’) thì dấu ‘-‘ sẽ được thay cho khoảng trắng.

Ví dụ:

#include <iostream> #include <iomanip> // for std::setw(n), std::setfill(ch), std::left, std::right using namespace std; int main() { cout << "Hello Duy Tan" << endl; cout << "Hello Duy Tan University" << endl << endl; cout << setw(5) << left << "ID"; // độ rộng 5 ký tự, canh trái ID cout << setw(30) << left << "Name"; // độ rộng 30 ký tự, canh trái Name cout << setw(20) << right << "Address" << endl; // độ rộng 20 ký tự, canh phải Address cout << setfill('-'); // set fill bằng ký tự '-' thay vì ' ' cout << setw(55) << "-" << endl; // fill 55 ký tự '-' // reset fill bằng ký tự ' ' cout << setfill(' '); // in thông tin theo format như trên cout << setw(5) << left << 1; cout << setw(30) << left << "Nguyen Doan Ngoc Giau"; cout << setw(20) << right << "Sai Gon" << endl; cout << setw(5) << left << 2; cout << setw(30) << left << "Tran Kim Long"; cout << setw(20) << right << "Da Lat" << endl; cout << setw(5) << left << 3; cout << setw(30) << left << "Nguyen Son Tung"; cout << setw(20) << right << "Dong Nai" << endl; return 0; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

Outputs:

Nhập, Xuất và Định dạng dữ liệu trong C++ (Input and Output) 

  • Các toán tử std::dec (thập phân), std::oct (bát phân), std::hex (thập lục phân) quy định số nguyên khi nhập xuất theo dạng thập phân, bát phân, hay thập lục phân.

Ví dụ:

#include <iostream> #include <iomanip> // for std::hex, std::oct, std::dec using namespace std; int main() { int n; cout << "Enter n (hexadecimal): "; cin >> hex >> n; // nhập số thập lục phân (hệ 16) cout << "Octal: " << oct << n << endl; // xuất số bát phân (hệ 8) cout << "Decimal: " << dec << n << endl; // xuất số thập phân (hệ 10) return 0; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

Outputs:

Nhập, Xuất và Định dạng dữ liệu trong C++ (Input and Output)

Thứ Năm, 26 tháng 11, 2020

Lập trình C++ Basic | #7. Tổng quan các kiểu dữ liệu trong C++

 Chương 2: CÁC KHÁI NIỆM CƠ BẢN TRONG C++

Do your c, c plus plus or c sharp assignments by Rajarjs

Tổng quan về kiểu dữ liệu cơ bản trong C++

Ở bài học trước Biến trong C++ (Variables), bạn đã biết biến (variable) là tên của một vùng trong bộ nhớ RAM, được sử dụng để lưu trữ thông tin. Bạn có thể gán thông tin cho một biến, và có thể lấy thông tin đó ra để sử dụng. Có rất nhiều loại thông tin (Ví dụ: thông tin dưới dạng số nguyên, số thực, ký tự, …), và trong C++, các biến cũng có thể lưu những loại thông tin khác nhau thông qua các kiểu dữ liệu khác nhau.

Kích thước của biến phụ thuộc vào kiểu dữ liệu của biến đó và quyết định số lượng thông tin mà biến đó lưu trữ. Khi bạn khai báo một biến, một vùng trong bộ nhớ sẽ dành cho biến đó. Ngày nay, việc khai báo biến với kích thước vài byte không là vấn đề gì, so với độ lớn của bộ nhớ máy tính. Nhưng nếu chương trình của bạn có số lượng biến lên tới hàng triệu, thì việc phải sử dụng biến với kích thước sao cho phù hợp là điều rất quan trọng.

Bảng bên dưới sẽ liệt kê những kiểu dữ liệu cơ bản trong C++. Kích thước kiểu dữ liệu tương ứng bên dưới chỉ là kích thước nhỏ nhất có thể của kiểu dữ liệu đó. Trong thực tế, kích thước này phụ thuộc vào từng compiler và kiến trúc máy tính.

Số tự nhiên và Số chấm động trong C++ (Integer, Floating point)


Để xác định kích thước của một kiểu dữ liệu trên một máy tính cụ thể, C++ cung cấp cho bạn toán tử sizeofToán tử sizeof là toán tử một ngôi, nhận vào một kiểu dữ liệu hoặc một biến, và trả về kích thước (byte) của của kiểu dữ liệu hoặc biến đó.

Ví dụ:

#include <iostream> using namespace std; int main() { cout << "bool:\t\t" << sizeof(bool) << " bytes" << endl; cout << "char:\t\t" << sizeof(char) << " bytes" << endl; cout << "wchar_t:\t" << sizeof(wchar_t) << " bytes" << endl; // C++11, may not be supported by your compiler cout << "char16_t:\t" << sizeof(char16_t) << " bytes" << endl; cout << "char32_t:\t" << sizeof(char32_t) << " bytes" << endl; cout << "short:\t\t" << sizeof(short) << " bytes" << endl; cout << "int:\t\t" << sizeof(int) << " bytes" << endl; cout << "long:\t\t" << sizeof(long) << " bytes" << endl; // C++11, may not be supported by your compiler cout << "long long:\t" << sizeof(long long) << " bytes" << endl; cout << "float:\t\t" << sizeof(float) << " bytes" << endl; cout << "double:\t\t" << sizeof(double) << " bytes" << endl; cout << "long double:\t" << sizeof(long double) << " bytes" << endl; // You can also use the sizeof operator on a variable name int n; cout << "n variable:\t" << sizeof(n) << " bytes" << endl; return 0; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

Chương trình bên trên khi thực thi trên Window 7 x64 (Visual studio 2015) sẽ cho ra kết quả:

Số tự nhiên và Số chấm động trong C++ (Integer, Floating point)

Một điều thú vị là toán tử sizeof là một trong 3 toán tử không phải là ký hiệu trong C++, 2 toán tử còn lại là new và delete sẽ được giới thiệu trong bài CẤP PHÁT ĐỘNG (Dynamic memory allocation).


Kiểu số nguyên (Integer)

Số nguyên là các số nguyên dương (1, 2, 3, …), các số đối (-1, -2, -3, …) và số 0. C++ có 5 loại số nguyên cơ bản để sử dụng:

Số tự nhiên và Số chấm động trong C++ (Integer, Floating point)

Chú ý: Char là một kiểu dữ liệu đặc biệt, nó vừa là kiểu số nguyên, cũng vừa là kiểu ký tự.

Chi tiết về tính chất ký tự của char sẽ được nói trong phần Character. Ở mục này, bạn tạm thời coi nó là một kiểu số nguyên bình thường.

Sự khác nhau giữa các kiểu số nguyên trên nằm ở kích thức. Kiểu có kích thước lớn sẽ lưu được những số nguyên lớn. Vùng giá trị của một kiểu số nguyên được xác định trên 2 yếu tố: kích thước và dấu của nó.

Số nguyên có dấu là những số nguyên dương (1, 2, 3, …), các số đối (-1, -2, -3, …) và số 0. Có 2 cách để khai báo một biến số nguyên có dấu:

// Khai báo không tường minh, thường được sử dụng char c; short s; int n; // Hoặc khai báo tường minh, sử dụng từ khóa signed signed char c; signed short s; signed int n;
1
2
3
4
5
6
7
8

Số nguyên không dấu là những số nguyên dương (1, 2, 3, …) và số 0. Đôi khi chương trình của bạn có những biến không cần lưu trữ các số âm (Ví dụ: chiều cao, cân nặng, độ dài, chỉ số danh sách, …). Để khai báo số nguyên không dấu, bạn sử dụng từ khóa unsigned. Ví dụ:

// Sử dụng từ khóa unsigned unsigned char uc; unsigned short us; unsigned int un;
1
2
3
4

Lưu ý: Một số nguyên không dấu không thể lưu trữ các số âm, nhưng nó có thể lưu trữ số dương lớn hơn gấp 2 lần số nguyên có dấu.

Bên dưới là bảng miền giá trị số nguyên

Số tự nhiên và Số chấm động trong C++ (Integer, Floating point)


Số chấm động (Floating point numbers) 

Trong C++, kiểu số chấm động đại diện cho số thực (Ví dụ: 69.9696, 3.14159, 0.00001 …), dùng để lưu trữ những số rất lớn hoặc rất nhỏ. Cấu trúc lưu trữ bên trong của số thực được thiết kế theo chuẩn số chấm động (floating-point) của IEEE.

Số chấm động không có từ khóa unsigned. Có 3 kiểu số chấm động khác nhau trong C++: float, double, long double.

Số tự nhiên và Số chấm động trong C++ (Integer, Floating point)

Chú ý: Một số môi trường lập trình đồng nhất kiểu long double với kiểu double nên kiểu này ít được sử dụng trong lập trình ứng dụng.

Cách để định nghĩa một biến số chấm động:

// Definitions of floating point numbers
float fVarName;
double dVarName2;
long double ldVarName3;

Chú ý:

Khi bạn sử dụng một hằng số dưới dạng một số chấm động, quy ước số đó phải có ít nhất 1 chữ số thập phân, điều này giúp phân biệt số chấm động và số nguyên.

Ví dụ:

// Initializations of floating point numbers float fVarName{4.0f};  // 4.0 means floating point (f suffix means float) double dVarName2{4.0}; // 4.0 means floating point (double by default) long double dVarName3{4.0L}; // 4.0 means floating point (L suffix means long double) int nVarName4{4};            // 4 means integer
1
2
3
4
5

Chú ý: Mặc định một hằng số thực sẽ là kiểu double. Để có một số thực kiểu float, bạn cần thêm hậu tố ‘f’.


Ký hiệu khoa học (Scientific notation)

Ký hiệu khoa học là cách xử lý những số rất lớn hoặc rất nhỏ. Ví dụ: chu kỳ xoay mặt trăng của Mộc Tinh là 152853.5047 s. Khi đó, bạn có thể viết bằng ký hiệu khoa học là 1.528535047 × 105 s. Hay một số khá quen thuộc với bạn như khối lượng của một electron là 9.1093822 x 10-31. Bên dưới là một số ví dụ khác:

24327 = 2.4327 x 104

7354 = 7.354 x 103

0,0078 = 7.8 x 103

0,00069 = 6.9 x 10-4

Chú ý: Số mũ sẽ là dương nếu dấu thập phân chuyển sang phải, là âm nếu dấu thập phân chuyển sang trái.

Trong C++, bạn có thể sử dụng ký hiệu khoa học để gán giá trị cho biến số chấm động. Dùng ký hiệu ‘e’ hoặc ‘E’ để thay cho 10.

Ví dụ:

// Initializations of floating point numbers double dVarName1{69000.0}; double dVarName2{6.9e4};     // 6.9e4 is equal to 69000.0 double dVarName3{0.00069}; double dVarName4{6.9E-4};    // 6.9e-4 is equal to 0.00069
1
2
3
4
5
6

Độ chính xác của số chấm động (Precision)

Số chấm động sẽ bao gồm những số hữu hạn và vô hạn. Đối với số vô hạn, nghĩa là phần thập phân sẽ có chiều dài vô hạn (Ví dụ: 1/6 = 0.1666666666666…, PI = 3.141592653589793…), nhưng bộ nhớ máy tính và kích thước kiểu dữ liệu thì hữu hạn. Nên biến số chấm động chỉ lưu được một độ chính xác nhất định, và phần số còn lại phía sau sẽ bị mất.

Trong C++, khi xuất một số chấm động, std::cout mặc định số có 6 chữ số. Những số ngoài phạm vi sẽ bị cắt bỏ và làm tròn lên 1 đơn vị nếu số bị cắt sau nó lớn hơn 5, hoặc số đó có thể được chuyển sang ký hiệu khoa học trong vài trường hợp tùy vào từng compiler. Ví dụ:

#include <iostream> using namespace std; int main() { double d; d = 9.87654321; cout << d << endl; d = 987.654321; cout << d << endl; d = 987654.321; cout << d << endl; d = 9876543.21; cout << d << endl; d = 0.0000987654321; cout << d << endl; d = 1.23456789; cout << d << endl; return 0; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

Chương trình bên trên khi thực thi trên Window 7 x64 (Visual studio 2015) sẽ cho ra kết quả:

Số tự nhiên và Số chấm động trong C++ (Integer, Floating point)

Mặc dù khi xuất một số chấm động, std::cout mặc định độ chính xác có 6 chữ số, nhưng bạn vẫn có thể thay đổi được độ chính xác này bằng cách sử dụng hàm std::setprecision() thuộc thư viện <iomanip>.

#include <iostream> #include <iomanip> // for std::setprecision() using namespace std; int main() { cout << std::setprecision(20); // Show 20 digits float f{ 9.66666666666666666666f }; // Initializations cout << f << endl; double d{ 9.66666666666666666666 }; // Initializations cout << d << endl; return 0; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

Kết quả thu được:

Số tự nhiên và Số chấm động trong C++ (Integer, Floating point)

Trong chương trình trên, ta đã thay đổi độ chính xác lên đến 20 chữ số thay vì là 6 chữ số như mặc định. Nhưng dù 2 biến float và double đều đã hiện đủ 20 chữ số, thì độ chính xác của nó vẫn không đến 20 chữ số.

Thông thường số chấm động kiểu float có độ chính xác đơn (single-precision), chính xác đến 7 chữ sốDouble có độ chính xác kép (double-precision), chính xác đến 16 chữ số. Đó là lý do tại sao chương trình trên lại có những số rác sau khoảng chính xác.

Độ chính xác của số chấm động không chỉ ảnh hưởng trên phần thập phân, mà nó có thể ảnh hưởng trên phần nguyên của những số có quá nhiều chữ số.

Ví dụ:

#include <iostream> #include <iomanip> // for std::setprecision() using namespace std; int main() { float f{ 123456789.0f }; cout << std::setprecision(9); // Show 9 digits cout << f << endl; return 0; }
1
2
3
4
5
6
7
8
9
10
11
12

Kết quả thu được:

Số tự nhiên và Số chấm động trong C++ (Integer, Floating point) 

Vì kiểu float có độ chính xác 7 chữ số, nên chương trình đã xuất ra 123.456.792, số này lớn hơn giá trị biến ban đầu rất nhiều. Do đó, bạn nên cẩn thận khi sử dụng kiểu float để lưu trữ những số cần một độ chính xác cao.

Chú ý: Bạn nên sử dụng kiểu double khi cần lưu trữ một số chấm động, hạn chế sử dụng float vì kiểu float có độ chính xác thấp sẽ dẫn tới số không chính xác.


Lỗi làm tròn số chấm động (Rounding errors)

Trong máy tính, số chấm động được lưu dưới hệ nhị phân.

Ví dụ: ta có phân số 1/10, = 0.1 trong hệ thập phân = 0.000110011(0011)… trong hệ nhị phân (lặp vô hạn). Ta thấy số 0.1 chuyển sang hệ nhị phân sẽ lặp vô hạn, nhưng độ chính xác của số chấm động là hữu hạn. Dẫn đến việc nó không thể được biểu diễn một cách chính xác như một giá trị nhị phân hữu hạn. Xét ví dụ:

#include <iostream> #include <iomanip> // for std::setprecision() using namespace std; int main() { double d{0.1}; cout << d << endl; // use default cout precision of 6 cout << std::setprecision(20); // show 20 digits cout << d << endl; return 0; }
1
2
3
4
5
6
7
8
9
10
11
12

Kết quả chương trình:

Số tự nhiên và Số chấm động trong C++ (Integer, Floating point)

Trong chương trình trên, ta có một biến double d{0.1}. Khi output với độ chính xác mặc định std::setprecision(6), ta nhận được chính xác 0.1. Nhưng khi output với std::setprecision(20), kết quả lại lớn hơn 0.1.

Kết quả cho thấy khi gán số 0.1 cho một biến số chấm động, biến đó sẽ không hoàn toàn bằng 0.1. Đó gọi là lỗi làm tròn số chấm động.

Xét tiếp ví dụ:

#include <iostream> #include <iomanip> // for std::setprecision() using namespace std; int main() { double d1{ 1.0 }; double d2{ 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 }; cout << std::setprecision(20); // show 20 digits cout << d1 << endl; cout << d2 << endl; return 0; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14

Kết quả chương trình:

 Số tự nhiên và Số chấm động trong C++ (Integer, Floating point)

Trong chương trình trên, trong toán học thì 2 biến d1 = d2, nhưng trong lập trình biến d1 > d2 vì lỗi làm tròn số dấu chấm động.

Tương tự, bạn hãy thử với trường hợp 0.1 + 0.7 = 0.8 ?

Tổng quan về kiểu ký tự (Character)

Ở bài học trước, bạn đã học về kiểu dữ liệu số học Integer và Floating point, những kiểu này dùng để giải quyết các bài toán trong chương trình, và lưu trữ nó. Một chương trình nếu chỉ hiển thị số thì người dùng có nhìn vào cũng không hiểu được, lúc này bạn sẽ cần những biến để lưu trữ ký tự, hay những câu thông báo cho người dùng. Để làm được điều đó, bạn cần học qua kiểu char trong C++.

Kiểu ký tự trong C++ (Character)

Trong C++, char là một kiểu dữ liệu đặc biệt, nó vừa là kiểu số nguyên, cũng vừa là kiểu ký tự. Do đó, kiểu char tuân thủ tất cả các quy tắc của một số nguyên bình thường (+-*/…). 

Một biến kiểu char sẽ có độ lớn 1 bytes (8 bits)dùng để lưu trữ một ký tự trong bảng mã ASCII như (a, b, c, … 1, 2, 3, …)

ASCII (American Standard Code for Information Interchange - Chuẩn mã trao đổi thông tin Hoa Kỳ) là bộ kí tự và bộ mã kí tự dựa trên bảng chữ cái La Tinh trong tiếng Anh. Bảng bên dưới mô tả đầy đủ ký tự ASCII:

Kiểu ký tự trong C++ (Character)

Kiểu ký tự trong C++ (Character)

Kiểu ký tự trong C++ (Character)

Có 2 loại ký tự trong bảng mã ASCII:

  • Ký tự không in được (ký tự điều kiển) có mã từ 0 -> 31 và 127 bao gồm các ký tự dùng để điều khiển hay dùng như: backspace (7), new line (10), escape (27), bell (7) …Có 2 loại ký tự trong bảng mã ASCII:
  • Ký tự in được có mã từ 32 -> 126 bao gồm các chữ cái (a, b, c, …), số (1, 2, 3, …), toán tử (+, -, …), dấu câu (~, {, ^ …)

Bảng mã ASCII chuẩn có 128 ký tự, bảng mã ASCII mở rộng có 255 ký tự.


Khai báo, khởi tạo và gán giá trị một biến ký tự

Để khai báo, khởi tạo và gán giá trị cho một biến kiểu ký tự trong C++, bạn sử dụng từ khóa char:

char ch1{ 'K' };// khởi tạo biến character với ký tự 'K' (mã ASCII 75) char ch2{ 75 }; // khởi tạo biến character với mã ASCII 75 (ký tự 'K') char ch3(75); // khởi tạo biến character với mã ASCII 75 (ký tự 'K') char ch4 = 'K'; // khởi tạo biến character với ký tự 'K' (mã ASCII 75) char ch5; // khai báo biến kiểu character ch1 = 75; // gán mã ASCII 75 (ký tự 'K') cho biến character
1
2
3
4
5
6
  • Chú ý 1: Vì bản chất của kiểu char cũng là số nguyên, nên khi khởi tạo hoặc gán giá trị cho biến kiểu char, bạn hoàn toàn có thể dùng số nguyên (mã ASCII) hoặc ký tự.
char ch1{ 75 }; // mã ASCII 75 (ký tự 'K') char ch2{ 'K' }; // ký tự 'K' (mã ASCII 75)
1
2
  • Chú ý 2: Ký tự số nguyên không giống với mã ASCII số nguyên, bạn cần phân biệt điều này để tránh nhầm lẫn khi khởi tạo một biến character.

Hai cách khởi tạo bên dưới hoàn toàn khác nhau:

char ch1{ '7' }; // khởi tạo biến character với ký tự '7' (mã ASCII 55) char ch2{ 7 }; // khởi tạo biến character với mã ASCII 7 (tiếng “beep”)
1
2

In ký tự ra màn hình

Dù khi khởi tạo hoặc gán giá trị cho biến kiểu charbạn hoàn toàn có thể dùng số nguyên (mã ASCII) hoặc ký tự. Nhưng khi in ra màn hình, nó hiển thị một ký tự ASCII thay vì một số nguyên.

#include <iostream> using namespace std; int main() { char ch1{ 75 }; // mã ASCII 75 (ký tự 'K') cout << ch1 << endl; // ký tự 'K' với mã ASCII 75 được hiển thị ra màn hình char ch2{ 'K' }; // ký tự 'K' (mã ASCII 75) cout << ch2 << endl; // ký tự 'K' trực tiếp được hiển thị ra màn hình return 0; }
1
2
3
4
5
6
7
8
9
10
11
12

Ở chương trình trên, 2 biến ch1ch2 đều cho kết quả K như nhau:

Kiểu ký tự trong C++ (Character)


In ký tự từ số nguyên và ngược lại (Casting)

Vì char là một kiểu dữ liệu đặc biệt, nó vừa là kiểu số nguyên, cũng vừa là kiểu ký tự. Đồng nghĩa với việc bạn có thể in ra màn hình ký tự hoặc mã ASCII của nó:

#include <iostream> #include <iomanip> // for std::setprecision() using namespace std; int main() { int n{ 75 }; cout << static_cast<char>(n) << endl; // in ký tự với mã ASCII 75 char ch{ 'K' }; cout << static_cast<int>(ch) << endl; // in mã ASCII của ký tự 'K' return 0; }
1
2
3
4
5
6
7
8
9
10
11
12
13

Outputs:

Kiểu ký tự trong C++ (Character)

Chương trình trên sử dụng kỹ thuật ép kiểu static cast trong C++ để in một ký tự từ một số nguyên và ngược lại. Cú pháp ép kiểu static cast:

static_cast<Type-id>(expression)

Lệnh static cast sẽ chuyển đổi giá trị của expression thành một giá trị có kiểu Type-id (char, int, double, …), và trả về giá trị đó.

  • Chú ý 1: Bản thân expression sẽ không bị ảnh hưởng sau khi ép kiểu (n vẫn là 75, ch vẫn là ‘K’).
  • Chú ý 2: Khi bạn ép kiểu từ int sang char, nếu giá trị int của bạn lớn hơn giới hạn kiểu char, vấn đề tràn số sẽ xảy ra.

Phạm vi bài học này chỉ đề cập cơ bản về ép kiểu và static_cast, vấn đề này sẽ được giới thiệu chi tiết hơn ở bài: ÉP KIỂU TƯỜNG MINH TRONG C++ (Explicit type conversion)


Escape sequences

Trong C++, có một số ký tự mang ý nghĩa đặc biệt dùng trong chuỗi hay mảng ký tự gọi là Escape sequence. Một escape sequence có cấu trúc gồm một dấu \’ (backslash), sau đó là một ký tự hoặc một số.

Bảng bên dưới liệt kê các Escape sequence trong C++:

Kiểu ký tự trong C++ (Character)

Một số ví dụ về escape sequence thường gặp:

std::cout << "Duy Tan University\nPNU CS211"; // '\n' di chuyển con trỏ xuống một dòng mới
1

Outputs:

 Kiểu ký tự trong C++ (Character)

std::cout << "Tab 1\tTab 2 "; // '\t' di chuyển con trỏ 1 tab
1

Outputs:

Kiểu ký tự trong C++ (Character)

Vẫn còn rất nhiều escape sequence, bạn hãy tự mình khám phá nhé.


Newline ‘\n’ và std::endl

Ở những bài học trước, bạn sử dụng std::endl để xuống dòng khi in một chuỗi, nhưng sau khi học về Escape sequence, bạn lại biết thêm một cách để xuống dòng nữa là sử dụng escape sequence \n’.

Nếu bạn viết một chương trình như bên dưới và sử dụng cả 2 cách, bạn sẽ có được kết quả như nhau:

std::cout << "Duy Tan University" << std::endl; std::cout << "PNU CS211\n";
1
2

Tuy nhiên, 2 cách này có thực sự giống nhau? Câu trả lời là khôngbản chất của std::endl được thể hiện ở 2 câu lệnh bên dưới:

std::cout << "Duy Tan University" << std::endl; // Tương đương với: std::cout << "Duy Tan University\n" << std::flush;
1
2
3
4
5

Trong C++, output stream thường dùng buffer, nghĩa là output data sẽ được lưu vào một vùng nhớ đệm, và output data sẽ được gửi đến output device vào thời điểm thích hợp (vì lý do hiệu suất). Với std::endl sẽ xóa output buffer mỗi khi nó được gọi, trong khi ‘\n’ thì không.

Vậy, khi nào nên sử dụng std::endl và \n’:

  • Nên sử dụng std::endl khi bạn cần đảm bảo output của bạn có ngay lập tức (Vd: khi viết một record vào một file, hoặc khi update một thanh tiến trình). Nhưng nên hạn chế sử dụng std::endl khi làm việc với file I/O để tránh việc phải flush buffer liên tục dẫn đến việc phải truy cập các file I/O thường xuyên (giảm hiệu suất).
  • Ngoài ra, những trường hợp khác nên sử dụng ‘\n’.

Dấu nháy đơn ‘DTU’ và dấu nháy kép “PNU”

Trong suốt bài học này, bạn có thể thấy các character luôn được đặt trong dấu nháy đơn (Vd: ‘K’, ‘t’, ‘e’, ‘a’, ‘m’). Một biến kiểu char sẽ lưu một ký tự, nếu bạn khởi tạo nhiều hơn 1 ký tự thì sẽ nhận được một thông báo lỗi như câu lệnh bên dưới:

char ch{ 'DTU' };      // Câu lệnh này sẽ cho ra một thông báo lỗi
1

Những ký tự đặt trong dấu nháy kép gọi là một chuỗi ký tự (Vd: “HowKteam.com”), chuỗi ký tự là một tập hợp của các ký tự được đặt trong dấu nháy kép. Câu lệnh bên dưới thể hiện “HowKteam.com” là một chuỗi ký tự (string).

std::cout << "PNU CS211";      // HowKteam.com là một chuỗi ký tự
1

Phạm vi bài học này chỉ đề cập đến ký tự, chuỗi ký tự (string) là tương đối phức tạp. Mình sẽ giới thiệu về chuỗi ký tự (string) trong bài CHUỔI KÝ TỰ TRONG C++ (String).

Tổng quan về kiểu luận lý (Boolean)

Ở bài học trước, bạn đã học về kiểu dữ liệu số học Integer và Floating point dùng để giải quyết các bài toán trong chương trình, và lưu trữ nó. Kiểu ký tự Character để lưu trữ ký tự. Ngoài ra, C++ vẫn còn một kiểu dữ liệu dùng để lưu trữ tính đúng, sai của một mệnh đề trong máy tính, nó được gọi là kiểu luận lý (boolean).

Trong C++, kiểu luận lý (boolean) có kích thước 1 byte, và chỉ lưu được 2 giá trị: true (1), false (0).

Để khai báo một biến boolean, bạn sử dụng từ khóa bool:

bool <tên biến>;    // Khai báo biến  có kiểu bool

Để khởi tạo và gán giá trị cho một biến boolean trong C++, bạn sử dụng từ khóa true (1), false (0):

bool b1(true); // true = 1 bool b2{ false }; // false = 0 bool b3 = 1; // 1 = true
1
2
3

Giống như toán tử một ngôi số đối (-), bạn hoàn toàn có thể phủ định giá trị luận lý từ true sang false và ngược lại bằng cách dùng toán tử NOT (!):

bool b1(!true); // b1 = NOT true = false (0) bool b2{ !false }; // b2 = NOT false = true (1) bool b3 = !1; // b3 = NOT 1 = false (0)
1
2
3

Giá trị của biến kiểu bool trong máy tính được biểu diễn dưới dạng 0 (false) hoặc 1 (true). Vì vậy, khi in một biến kiểu bool, giá trị được in ra sẽ là 0 (false) hoặc 1 (true):

#include <iostream> using namespace std; int main() { bool b1{ true }; // b1 is true cout << b1 << endl; // in 1 ra màn hình cout << !b1 << endl; // in 0 ra màn hình bool b2{ false }; // b1 is false cout << b2 << endl; // in 0 ra màn hình cout << !b2 << endl; // in 1 ra màn hình return 0; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

Outputs:

Kiểu luận lý và cơ bản về Câu điều kiện If trong C++ (Boolean and If statements basic)

Chú ý: khi khởi tạo hoặc gán giá trị dưới dạng số nguyên cho biến kiểu bool, mọi giá trị khác 0 sẽ mặc định là true (1).

bool b1(6); // b1 = true (1) bool b3 = !-9; // b2 = NOT true = false (0)
1
2

Nếu bạn muốn giá trị của biến boolean khi được in ra là true hoặc false thay vì 0 hoặc 1, bạn có thể sử dụng std::boolalpha. Nếu bạn muốn in giá trị biến kiểu boolean như mặc định thì có thể sử dụng std::noboolalpha.

#include <iostream> using namespace std; int main() { cout << std::boolalpha; // in "true" hoặc "false" với kiểu bool cout << true << endl; // in "true" ra màn hình cout << false << endl; // in "false" ra màn hình cout << std::noboolalpha; // in 1 hoặc 0 với kiểu bool cout << true << endl; // in 1 ra màn hình cout << false << endl; // in 0 ra màn hình return 0; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

Outputs:

 Kiểu luận lý và cơ bản về Câu điều kiện If trong C++ (Boolean and If statements basic)

Bài tập chuỗi trong C++

  Bài tập chuỗi trong C++   (7) 668 lượt xem Chuỗi (String) trong C/C++ là một mảng ký tự được kết thúc bởi \0 (ký tự null). Dưới đây là các...