Bài 3.2: Bài tập thực hành kiểu dữ liệu cơ bản trong C++

Bài 3.2: Bài tập thực hành kiểu dữ liệu cơ bản trong C++
Chào mừng các bạn quay trở lại với chuỗi bài học lập trình C++ cùng FullhouseDev! Sau khi đã tìm hiểu về cách nhập xuất dữ liệu cơ bản, hôm nay chúng ta sẽ đi sâu vào một khái niệm cực kỳ quan trọng và là nền tảng của mọi ngôn ngữ lập trình: kiểu dữ liệu.
Trong C++, mỗi biến (variable) mà chúng ta sử dụng để lưu trữ thông tin đều cần được khai báo với một kiểu dữ liệu cụ thể. Kiểu dữ liệu này sẽ cho trình biên dịch biết:
- Kích thước của vùng nhớ cần cấp phát cho biến đó.
- Loại dữ liệu mà biến đó có thể chứa (số nguyên, số thực, ký tự, logic...).
- Những thao tác nào có thể thực hiện trên biến đó.
Việc hiểu rõ và sử dụng đúng kiểu dữ liệu là chìa khóa để viết code hiệu quả, tránh lỗi và quản lý bộ nhớ tốt. Bài hôm nay sẽ tập trung vào các kiểu dữ liệu cơ bản nhất và cách thực hành chúng qua các ví dụ cụ thể.
Các Kiểu Dữ Liệu Cơ Bản Nhất trong C++
C++ cung cấp một bộ sưu tập các kiểu dữ liệu nguyên thủy (primitive data types) mà từ đó chúng ta có thể xây dựng nên các cấu trúc phức tạp hơn. Dưới đây là những kiểu dữ liệu cơ bản mà chúng ta sẽ làm việc hôm nay:
- Số nguyên (Integer Types): Dùng để lưu trữ các số không có phần thập phân.
**int**
: Kiểu số nguyên thông dụng nhất, thường có kích thước 4 byte (trên hầu hết các hệ thống hiện đại), có thể chứa cả số dương, số âm và số 0.**short**
: Số nguyên kích thước nhỏ hơnint
, thường 2 byte.**long**
: Số nguyên kích thước lớn hơn hoặc bằngint
, thường 4 hoặc 8 byte.**long long**
: Số nguyên kích thước lớn nhất, thường 8 byte, dùng cho các số cực lớn.- (Có thể thêm
unsigned
vào trước các kiểu trên để chỉ lưu trữ số không âm).
- Số thực (Floating-Point Types): Dùng để lưu trữ các số có phần thập phân.
**float**
: Số thực độ chính xác đơn (single-precision), thường 4 byte.**double**
: Số thực độ chính xác kép (double-precision), thường 8 byte, có độ chính xác cao hơnfloat
.**long double**
: Số thực độ chính xác mở rộng, kích thước có thể khác nhau (8, 12, 16 byte), độ chính xác cao nhất.
- Ký tự (Character Type): Dùng để lưu trữ một ký tự đơn lẻ.
**char**
: Thường 1 byte, lưu trữ một ký tự theo bảng mã ASCII (hoặc Unicode).
- Logic (Boolean Type): Dùng để lưu trữ giá trị logic đúng/sai.
**bool**
: Chỉ có hai giá trị làtrue
(đúng) hoặcfalse
(sai).
Thực Hành với Từng Kiểu Dữ Liệu
Để hiểu rõ hơn, chúng ta hãy đi qua các ví dụ thực hành cho từng loại.
1. Thực Hành với Số Nguyên (Integer Types)
Số nguyên là kiểu dữ liệu phổ biến nhất cho việc đếm, tính toán các giá trị rời rạc.
Ví dụ 1: Khai báo và gán giá trị
#include <iostream>
int main() {
int so_luong_tao = 10;
short diem_kiem_tra = 95;
long dan_so_the_gioi = 8000000000L; // Dùng hậu tố 'L' cho long
long long so_hat_cat_trong_sa_mac = 1000000000000000LL; // Dùng hậu tố 'LL' cho long long
cout << "So luong tao: " << so_luong_tao << endl;
cout << "Diem kiem tra: " << diem_kiem_tra << endl;
cout << "Dan so the gioi (uoc luong): " << dan_so_the_gioi << endl;
cout << "So hat cat (vi du): " << so_hat_cat_trong_sa_mac << endl;
return 0;
}
- Giải thích: Chúng ta khai báo các biến với các kiểu
int
,short
,long
,long long
và gán cho chúng các giá trị phù hợp với phạm vi của từng kiểu. Chú ý hậu tốL
vàLL
giúp trình biên dịch hiểu rõ chúng ta đang muốn sử dụng kiểulong
vàlong long
cho các giá trị số lớn.endl
dùng để xuống dòng sau khi in.
Ví dụ 2: Các phép toán số học cơ bản
#include <iostream>
int main() {
int a = 25;
int b = 7;
int tong = a + b; // Cộng
int hieu = a - b; // Trừ
int tich = a * b; // Nhân
int thuong_nguyen = a / b; // Chia lấy phần nguyên
int so_du = a % b; // Chia lấy phần dư (modulo)
cout << "a = " << a << ", b = " << b << endl;
cout << "Tong: " << tong << endl; // Output: 32
cout << "Hieu: " << hieu << endl; // Output: 18
cout << "Tich: " << tich << endl; // Output: 175
cout << "Thuong nguyen: " << thuong_nguyen << endl; // Output: 3 (25 / 7 = 3 dư 4)
cout << "So du: " << so_du << endl; // Output: 4
return 0;
}
- Giải thích: Ví dụ này minh họa các phép toán cộng (
+
), trừ (-
), nhân (*
), chia lấy phần nguyên (/
) và chia lấy phần dư (%
) trên hai biến kiểuint
. Lưu ý quan trọng: Phép chia (/
) giữa hai số nguyên sẽ luôn cho kết quả là một số nguyên bằng cách loại bỏ phần thập phân (không làm tròn).
2. Thực Hành với Số Thực (Floating-Point Types)
Số thực được sử dụng khi bạn cần làm việc với các giá trị có phần thập phân, như tính toán tiền tệ, đo lường, khoa học, v.v.
Ví dụ 3: Khai báo và tính toán
#include <iostream>
#include <iomanip> // Để sử dụng fixed và setprecision
int main() {
float gia_san_pham = 199.99f; // Dùng hậu tố 'f' cho float
double ban_kinh_hinh_tron = 5.5;
// Tính diện tích hình tròn: Pi * R^2
const double PI = 3.14159; // const double cho giá trị hằng số Pi
double dien_tich = PI * ban_kinh_hinh_tron * ban_kinh_hinh_tron;
cout << "Gia san pham: " << gia_san_pham << endl;
cout << "Ban kinh hinh tron: " << ban_kinh_hinh_tron << endl;
// In dien tich voi do chinh xac 2 chu so thap phan
cout << fixed << setprecision(2);
cout << "Dien tich hinh tron: " << dien_tich << endl; // Output: 95.03
return 0;
}
- Giải thích: Chúng ta khai báo biến
gia_san_pham
kiểufloat
(sử dụng hậu tốf
) vàban_kinh_hinh_tron
kiểudouble
.double
thường được ưa chuộng hơnfloat
vì độ chính xác cao hơn, trừ khi bộ nhớ là vấn đề cực kỳ quan trọng. Phép toán nhân (*
) hoạt động như mong đợi.fixed
vàsetprecision(2)
từ thư viện<iomanip>
được dùng để định dạng đầu ra số thực với 2 chữ số sau dấu thập phân.
3. Thực Hành với Ký Tự (Character Type)
Kiểu char
lưu trữ một ký tự duy nhất. Ký tự được bao quanh bởi dấu nháy đơn (' '
).
Ví dụ 4: Lưu trữ và hiển thị ký tự
#include <iostream>
int main() {
char chu_cai_dau = 'A';
char ki_tu_dac_biet = '$';
char chu_so_ky_tu = '7';
cout << "Chu cai dau: " << chu_cai_dau << endl;
cout << "Ki tu dac biet: " << ki_tu_dac_biet << endl;
cout << "Chu so ky tu: " << chu_so_ky_tu << endl;
// In ma ASCII cua ky tu
cout << "Ma ASCII cua '" << chu_cai_dau << "': " << static_cast<int>(chu_cai_dau) << endl; // Output: 65
return 0;
}
- Giải thích: Biến kiểu
char
có thể lưu trữ bất kỳ ký tự nào được đặt trong dấu nháy đơn. Điều thú vị là ký tự cũng có giá trị số tương ứng (theo bảng mã ASCII). Bằng cách ép kiểu (type casting)static_cast<int>(chu_cai_dau)
, chúng ta có thể hiển thị giá trị số (ASCII) của ký tự đó.
4. Thực Hành với Logic (Boolean Type)
Kiểu bool
chỉ có hai trạng thái: true
(đúng) hoặc false
(sai). Chúng rất quan trọng trong các cấu trúc điều khiển luồng (như if
, while
).
Ví dụ 5: Sử dụng giá trị boolean
#include <iostream>
int main() {
bool la_sinh_vien = true;
bool da_tot_nghiep = false;
cout << "La sinh vien: " << la_sinh_vien << endl; // Output: 1 (true thường được hiển thị là 1)
cout << "Da tot nghiep: " << da_tot_nghiep << endl; // Output: 0 (false thường được hiển thị là 0)
// Sử dụng boolalpha để hiển thị true/false thay vì 1/0
cout << boolalpha;
cout << "La sinh vien (boolalpha): " << la_sinh_vien << endl; // Output: true
cout << "Da tot nghiep (boolalpha): " << da_tot_nghiep << endl; // Output: false
return 0;
}
- Giải thích: Biến kiểu
bool
được gán trực tiếp giá trịtrue
hoặcfalse
. Mặc định, khi in ra,true
thường được hiển thị là1
vàfalse
là0
. Tuy nhiên, bạn có thể sử dụngboolalpha
để thay đổi cài đặt luồng xuất, khiến chúng hiển thị trực tiếp chuỗi"true"
và"false"
.
Thực Hành Tổng Hợp: Ép Kiểu Dữ Liệu (Type Casting)
Đôi khi, bạn cần chuyển đổi giá trị từ kiểu dữ liệu này sang kiểu dữ liệu khác. Đây gọi là ép kiểu (type casting). C++ có thể tự động ép kiểu (implicit casting) trong một số trường hợp an toàn, nhưng thường bạn cần ép kiểu rõ ràng (explicit casting) để đảm bảo kết quả mong muốn.
Ví dụ 6: Ép kiểu giữa số nguyên và số thực
Hãy quay lại bài toán chia. Nếu chúng ta muốn lấy kết quả chia có phần thập phân từ hai số nguyên thì sao?
#include <iostream>
#include <iomanip>
int main() {
int so_chia = 25;
int so_bi_chia = 7;
// Chia lay phan nguyen (nhu vi du 2)
int ket_qua_nguyen = so_chia / so_bi_chia;
cout << "Ket qua nguyen (int / int): " << ket_qua_nguyen << endl; // Output: 3
// Chia lay phan thuc - Cach 1: Ep kieu mot trong hai so truoc khi chia
double ket_qua_thuc_c1 = static_cast<double>(so_chia) / so_bi_chia;
cout << fixed << setprecision(2);
cout << "Ket qua thuc (double / int): " << ket_qua_thuc_c1 << endl; // Output: 3.57
// Chia lay phan thuc - Cach 2: Ep kieu ca bieu thuc sau khi chia (khong nen lam the nay cho phep chia)
// double ket_qua_thuc_c2 = static_cast<double>(so_chia / so_bi_chia);
// cout << "Ket qua thuc (ep kieu sau): " << ket_qua_thuc_c2 << endl; // Output: 3.00 (Sai!)
// Chia lay phan thuc - Cach 3: Khai bao bien la so thuc ngay tu dau
double so_chia_d = 25.0;
double so_bi_chia_d = 7.0;
double ket_qua_thuc_c3 = so_chia_d / so_bi_chia_d;
cout << "Ket qua thuc (double / double): " << ket_qua_thuc_c3 << endl; // Output: 3.57
return 0;
}
- Giải thích:
- Khi bạn chia
int
choint
, kết quả làint
(lấy phần nguyên). - Để có kết quả
double
, ít nhất một trong hai toán hạng (số bị chia hoặc số chia) phải là kiểu số thực trước khi phép chia diễn ra. static_cast<double>(so_chia)
ép kiểuso_chia
từint
sangdouble
. Khi đó, phép chia trở thànhdouble / int
. C++ sẽ tự động nâng cấp (promote)so_bi_chia
thànhdouble
để thực hiện phép chiadouble / double
, cho kết quả làdouble
.- Quan trọng: Ép kiểu kết quả sau khi chia nguyên (
static_cast<double>(so_chia / so_bi_chia)
) là sai vì phép chia nguyên đã được thực hiện trước, làm mất đi phần thập phân rồi. - Cách tốt nhất nếu bạn biết trước sẽ cần kết quả thực là khai báo biến ngay từ đầu với kiểu số thực (
double
).
- Khi bạn chia
Ví dụ 7: Một bài toán thực tế nhỏ - Tính chỉ số BMI
Chỉ số BMI (Body Mass Index) được tính bằng công thức: cân nặng (kg) / (chiều cao (m) * chiều cao (m))
. Đây là một ví dụ điển hình cần sử dụng cả số nguyên và số thực, và có thể cần ép kiểu.
#include <iostream>
#include <iomanip>
int main() {
int can_nang_kg = 70; // Can nang bang so nguyen
int chieu_cao_cm = 175; // Chieu cao bang so nguyen (cm)
// Chuyen chieu cao tu cm sang met (so thuc)
double chieu_cao_m = static_cast<double>(chieu_cao_cm) / 100;
// Tinh BMI. Can nang la int, chieu cao la double.
// Phep chia int / double se tu dong chuyen int sang double.
double bmi = static_cast<double>(can_nang_kg) / (chieu_cao_m * chieu_cao_m);
cout << "Can nang: " << can_nang_kg << " kg" << endl;
cout << "Chieu cao: " << chieu_cao_cm << " cm (" << chieu_cao_m << " m)" << endl;
cout << fixed << setprecision(2);
cout << "Chi so BMI cua ban: " << bmi << endl; // Output: Khoang 22.86 (tùy hệ thống)
return 0;
}
- Giải thích:
- Chúng ta có cân nặng và chiều cao dưới dạng số nguyên.
- Công thức BMI yêu cầu chiều cao tính bằng mét và là số thực. Chúng ta ép kiểu
chieu_cao_cm
sangdouble
rồi chia cho 100 để được chiều cao dạngdouble
tính bằng mét. - Khi tính
bmi
, chúng ta ép kiểucan_nang_kg
sangdouble
để đảm bảo toàn bộ phép tính diễn ra với số thực, cho kết quả chính xác. Nếu không ép kiểucan_nang_kg
, phép chia đầu tiên (can_nang_kg / chieu_cao_m
) sẽ vẫn được thực hiện dưới dạng số thực (vì một toán hạng làdouble
), nhưng việc ép kiểu rõ ràng giúp code dễ đọc và minh bạch hơn ý định của lập trình viên.
Lời Khuyên Khi Sử Dụng Kiểu Dữ Liệu
- Chọn kiểu phù hợp: Đừng lúc nào cũng dùng
int
hoặcdouble
. Hãy nghĩ xem dữ liệu của bạn là gì và cần phạm vi/độ chính xác như thế nào. Dùngshort
hoặcint
cho số đếm nhỏ,long long
cho số rất lớn,float
cho số thực cần ít bộ nhớ,double
cho hầu hết các tính toán số thực cần độ chính xác cao. - Cẩn thận với phép chia số nguyên: Luôn nhớ
int / int
cho kết quảint
. Nếu muốn kết quả số thực, hãy đảm bảo ít nhất một toán hạng là số thực trước khi phép chia diễn ra. - Hiểu về phạm vi: Mỗi kiểu dữ liệu có một phạm vi giá trị nhất định. Nếu lưu trữ một số vượt quá phạm vi, sẽ xảy ra hiện tượng tràn số (overflow), dẫn đến kết quả không mong muốn.
- Ép kiểu khi cần: Sử dụng
static_cast<Kieu_Moi>(bien_cu)
để ép kiểu một cách rõ ràng, giúp code dễ hiểu và kiểm soát được quá trình chuyển đổi.
Comments