Bài 1.3: Toán tử số học, so sánh và logic trong C++

Bài 1.3: Toán tử số học, so sánh và logic trong C++
Chào mừng trở lại với series học C++ cùng FullhouseDev! Sau khi đã làm quen với cách nhập xuất dữ liệu cơ bản sử dụng cin
và cout
, đã đến lúc chúng ta đi sâu vào trái tim của mọi chương trình: các toán tử.
Toán tử trong C++ giống như những "động từ" giúp chúng ta thực hiện các hành động trên dữ liệu. Chúng cho phép chúng ta tính toán, so sánh giá trị và đưa ra quyết định dựa trên các điều kiện. Hiểu rõ về toán tử là cực kỳ quan trọng để viết được những chương trình mạnh mẽ và linh hoạt.
Trong bài viết này, chúng ta sẽ tập trung vào ba nhóm toán tử phổ biến và thiết yếu nhất: toán tử số học, toán tử so sánh, và toán tử logic. Hãy cùng khám phá sức mạnh của chúng nhé!
1. Toán tử Số học (Arithmetic Operators)
Toán tử số học được sử dụng để thực hiện các phép toán toán học cơ bản trên các giá trị số (biến hoặc hằng số). Đây là những toán tử quen thuộc mà bạn đã gặp từ rất lâu rồi!
Các toán tử số học chính trong C++ bao gồm:
+
: Cộng-
: Trừ*
: Nhân/
: Chia%
: Chia lấy phần dư (Modulo)
Hãy xem một ví dụ đơn giản:
#include <iostream>
int main() {
using namespace std;
int a = 20;
int b = 7;
int tong, hieu, tich, thuong, du;
tong = a + b;
hieu = a - b;
tich = a * b;
thuong = a / b;
du = a % b;
cout << "Tong: " << tong << endl;
cout << "Hieu: " << hieu << endl;
cout << "Tich: " << tich << endl;
cout << "Thuong (so nguyen): " << thuong << endl;
cout << "Phan du: " << du << endl;
double c = 20.0;
double d = 7.0;
double thuongThuc = c / d;
cout << "Thuong (so thuc): " << thuongThuc << endl;
return 0;
}
Tong: 27
Hieu: 13
Tich: 140
Thuong (so nguyen): 2
Phan du: 6
Thuong (so thuc): 2.85714
Giải thích:
- Ví dụ trên minh họa cách sử dụng các toán tử số học cơ bản.
- Đối với phép chia (
/
), kết quả phụ thuộc vào kiểu dữ liệu của các toán hạng:- Nếu cả hai toán hạng là số nguyên (
int
), kết quả sẽ là phần nguyên của phép chia (phần thập phân bị cắt bỏ).20 / 7
cho kết quả2
. - Nếu ít nhất một toán hạng là số thực (
double
,float
), kết quả sẽ là số thực.20.0 / 7.0
cho kết quả xấp xỉ2.857
.
- Nếu cả hai toán hạng là số nguyên (
- Toán tử
%
(modulo) chỉ hoạt động với các toán hạng là số nguyên và trả về phần dư của phép chia.20 % 7
có nghĩa là 20 chia 7 được 2 dư 6, nên kết quả là6
.
Lưu ý: Phép chia cho số 0
- Nếu bạn cố gắng chia cho
0
, chương trình sẽ gặp lỗi và dừng lại. C++ không cho phép phép chia cho số 0 vì nó không xác định. - Luôn kiểm tra điều kiện trước khi thực hiện phép chia để tránh lỗi này.
- Ví dụ: Trước khi thực hiện
thuong = a / b;
, bạn nên kiểm traif (b != 0)
để đảm bảob
không phải là0
.if (b != 0) { thuong = a / b; } else { cout << "Loi: Khong the chia cho 0!" << endl; }
1.1. Toán tử Gán Kết hợp (Compound Assignment Operators)
C++ cung cấp các toán tử gán kết hợp để viết tắt các phép gán có liên quan đến toán tử số học. Chúng giúp code ngắn gọn và dễ đọc hơn.
+=
: Cộng rồi gán (e.g.,x += 5;
tương đươngx = x + 5;
)-=
: Trừ rồi gán (e.g.,y -= 3;
tương đươngy = y - 3;
)*=
: Nhân rồi gán (e.g.,z *= 2;
tương đươngz = z * 2;
)/=
: Chia rồi gán (e.g.,w /= 4;
tương đươngw = w / 4;
)%=
: Modulo rồi gán (e.g.,v %= 10;
tương đươngv = v % 10;
)
Ví dụ:
#include <iostream>
int main() {
using namespace std;
int dem = 10;
dem += 5;
cout << "Sau khi dem += 5: " << dem << endl;
dem *= 2;
cout << "Sau khi dem *= 2: " << dem << endl;
return 0;
}
Sau khi dem += 5: 15
Sau khi dem *= 2: 30
Giải thích:
- Toán tử
+=
và*=
thực hiện phép toán tương ứng rồi gán kết quả trở lại biến ở bên trái. Đây là cách viết tắt phổ biến và được khuyến khích sử dụng.
1.2. Toán tử Tăng/Giảm (Increment/Decrement Operators)
Các toán tử ++
(tăng) và --
(giảm) là đặc trưng của C/C++ và rất hữu ích khi bạn cần tăng hoặc giảm giá trị của một biến đi một đơn vị. Chúng thường được sử dụng trong các vòng lặp.
Điều đặc biệt là mỗi toán tử này có hai dạng:
- Prefix (Tiền tố):
++variable
hoặc--variable
. Toán tử thực hiện việc tăng/giảm giá trị trước khi sử dụng giá trị đó trong biểu thức. - Postfix (Hậu tố):
variable++
hoặcvariable--
. Toán tử sử dụng giá trị hiện tại của biến trước khi thực hiện việc tăng/giảm giá trị.
Sự khác biệt này rất quan trọng khi toán tử được sử dụng trong một biểu thức phức tạp hơn là chỉ đứng một mình.
Ví dụ:
#include <iostream>
int main() {
using namespace std;
int i = 5;
int j = 5;
cout << "Gia tri ban dau cua i: " << i << endl;
cout << "i++ (hau to) trong cout: " << i++ << endl;
cout << "Gia tri cua i sau i++: " << i << endl;
cout << "\nGia tri ban dau cua j: " << j << endl;
cout << "++j (tien to) trong cout: " << ++j << endl;
cout << "Gia tri cua j sau ++j: " << j << endl;
int a = 10;
int b = ++a;
cout << "\nKhi b = ++a: a = " << a << ", b = " << b << endl;
int x = 10;
int y = x++;
cout << "Khi y = x++: x = " << x << ", y = " << y << endl;
return 0;
}
Gia tri ban dau cua i: 5
i++ (hau to) trong cout: 5
Gia tri cua i sau i++: 6
Gia tri ban dau cua j: 5
++j (tien to) trong cout: 6
Gia tri cua j sau ++j: 6
Khi b = ++a: a = 11, b = 11
Khi y = x++: x = 11, y = 10
Giải thích:
- Sự khác biệt giữa tiền tố và hậu tố nằm ở thời điểm biến được cập nhật giá trị so với thời điểm giá trị của biểu thức được xác định.
- Với
++variable
hoặc--variable
, biến được thay đổi ngay lập tức và giá trị mới này được sử dụng trong biểu thức. - Với
variable++
hoặcvariable--
, giá trị hiện tại của biến được sử dụng trong biểu thức, và sau đó biến mới được thay đổi.
- Với
2. Toán tử So sánh (Comparison/Relational Operators)
Toán tử so sánh, còn gọi là toán tử quan hệ, được sử dụng để so sánh hai giá trị. Kết quả của một phép so sánh luôn là một giá trị boolean: true
(đúng) hoặc false
(sai). Trong C++, true
thường được biểu diễn bởi số 1
, và false
là 0
.
Các toán tử so sánh chính:
==
: Bằng (Equal to)!=
: Không bằng (Not equal to)>
: Lớn hơn (Greater than)<
: Nhỏ hơn (Less than)>=
: Lớn hơn hoặc bằng (Greater than or equal to)<=
: Nhỏ hơn hoặc bằng (Less than or equal to)
Ví dụ:
#include <iostream>
int main() {
using namespace std;
int a = 15;
int b = 10;
cout << "a == b: " << (a == b) << endl;
cout << "a != b: " << (a != b) << endl;
cout << "a > b: " << (a > b) << endl;
cout << "a < b: " << (a < b) << endl;
cout << "a >= b: " << (a >= b) << endl;
cout << "a <= b: " << (a <= b) << endl;
bool lonHon = (a > b);
cout << "lonHon: " << lonHon << endl;
int c = 15;
cout << "a == c: " << (a == c) << endl;
return 0;
}
a == b: 0
a != b: 1
a > b: 1
a < b: 0
a >= b: 1
a <= b: 0
lonHon: 1
a == c: 1
Giải thích:
- Mỗi biểu thức sử dụng toán tử so sánh sẽ trả về
true
hoặcfalse
. - Khi in ra console bằng
cout
, giá trịtrue
được hiển thị là1
vàfalse
là0
theo mặc định. - Các toán tử so sánh là nền tảng cho các cấu trúc điều khiển luồng như câu lệnh
if
và vòng lặp (for
,while
), nơi chúng ta cần kiểm tra các điều kiện.
LƯU Ý QUAN TRỌNG: Đừng nhầm lẫn giữa toán tử gán =
và toán tử so sánh ==
.
=
dùng để gán giá trị (a = 10;
).==
dùng để kiểm tra sự bằng nhau (a == 10
).
Sử dụng sai một trong hai có thể dẫn đến lỗi cú pháp hoặc logic khó phát hiện!
3. Toán tử Logic (Logical Operators)
Toán tử logic được sử dụng để kết hợp hoặc thao tác với các giá trị boolean (true
/false
). Chúng cho phép bạn xây dựng các điều kiện phức tạp hơn bằng cách kết hợp kết quả của nhiều phép so sánh.
Ba toán tử logic chính là:
&&
: AND (và) - Kết quả làtrue
chỉ khi cả hai toán hạng đều làtrue
.||
: OR (hoặc) - Kết quả làtrue
khi ít nhất một trong hai toán hạng làtrue
.!
: NOT (phủ định) - Đảo ngược giá trị boolean của toán hạng.!true
làfalse
,!false
làtrue
.
Ví dụ:
#include <iostream>
int main() {
using namespace std;
int tuoi = 22;
double diem = 3.5;
bool coViec = false;
bool dk1 = (tuoi > 18) && (diem > 3.0);
cout << "(tuoi > 18) && (diem > 3.0): " << dk1 << endl;
bool dk2 = (tuoi > 25) && coViec;
cout << "(tuoi > 25) && coViec: " << dk2 << endl;
bool dk3 = (tuoi < 20) || coViec;
cout << "(tuoi < 20) || coViec: " << dk3 << endl;
bool dk4 = (tuoi > 20) || (diem < 3.0);
cout << "(tuoi > 20) || (diem < 3.0): " << dk4 << endl;
bool dk5 = !coViec;
cout << "!coViec: " << dk5 << endl;
bool dk6 = !(tuoi > 30);
cout << "!(tuoi > 30): " << dk6 << endl;
bool dkPhucTap = ((tuoi > 18) && (diem > 3.0)) || !coViec;
cout << "((tuoi > 18) && (diem > 3.0)) || !coViec: " << dkPhucTap << endl;
return 0;
}
(tuoi > 18) && (diem > 3.0): 1
(tuoi > 25) && coViec: 0
(tuoi < 20) || coViec: 0
(tuoi > 20) || (diem < 3.0): 1
!coViec: 1
!(tuoi > 30): 1
((tuoi > 18) && (diem > 3.0)) || !coViec: 1
Giải thích:
- Toán tử
&&
yêu cầu cả hai biểu thức con (trái và phải) phải cùng trả vềtrue
thì kết quả cuối cùng mới làtrue
. Chỉ cần một trong hai làfalse
, kết quả sẽ làfalse
. - Toán tử
||
yêu cầu chỉ cần một trong hai biểu thức con trả vềtrue
thì kết quả cuối cùng sẽ làtrue
. Chỉ khi cả hai cùng làfalse
, kết quả mới làfalse
. - Toán tử
!
đơn giản là đổitrue
thànhfalse
và ngược lại. - Bạn có thể kết hợp nhiều toán tử logic và toán tử so sánh để tạo ra các điều kiện kiểm tra phức tạp. Dấu ngoặc đơn
()
giúp xác định rõ ràng thứ tự ưu tiên đánh giá các phần của biểu thức.
3.1. Đánh giá Rút gọn (Short-Circuit Evaluation)
Một điểm thú vị và quan trọng về toán tử &&
và ||
trong C++ là chúng sử dụng kỹ thuật "đánh giá rút gọn" (short-circuit evaluation).
- Với
&&
: Nếu biểu thức bên trái đã làfalse
, toàn bộ biểu thức&&
chắc chắn sẽ làfalse
. Trình biên dịch C++ sẽ không cần đánh giá biểu thức bên phải nữa. - Với
||
: Nếu biểu thức bên trái đã làtrue
, toàn bộ biểu thức||
chắc chắn sẽ làtrue
. Trình biên dịch C++ sẽ không cần đánh giá biểu thức bên phải nữa.
Kỹ thuật này giúp tối ưu hóa hiệu suất và đôi khi ngăn ngừa lỗi. Ví dụ, bạn có thể kiểm tra một con trỏ có khác nullptr
trước khi truy cập vào vùng nhớ mà nó trỏ tới trong cùng một biểu thức &&
.
4. Độ ưu tiên và Kết hợp (Precedence and Associativity)
Khi bạn kết hợp nhiều loại toán tử trong một biểu thức, C++ có các quy tắc về độ ưu tiên (precedence) và tính kết hợp (associativity) để xác định thứ tự các phép toán được thực hiện.
- Độ ưu tiên: Toán tử có độ ưu tiên cao hơn sẽ được thực hiện trước. Ví dụ, phép nhân và chia (
*
,/
,%
) có độ ưu tiên cao hơn phép cộng và trừ (+
,-
). - Tính kết hợp: Nếu có nhiều toán tử cùng độ ưu tiên xuất hiện liền kề, tính kết hợp (trái sang phải hoặc phải sang trái) sẽ xác định thứ tự thực hiện. Hầu hết các toán tử số học và so sánh có tính kết hợp từ trái sang phải. Toán tử gán và một số toán tử khác có tính kết hợp từ phải sang trái.
Bạn không cần nhớ hết tất cả các quy tắc ưu tiên phức tạp này. Cách tốt nhất để đảm bảo biểu thức của bạn được đánh giá đúng như ý muốn và để code dễ đọc hơn là sử dụng dấu ngoặc đơn ()
để nhóm các phép toán lại. Phần trong ngoặc đơn sẽ luôn được ưu tiên thực hiện trước.
Ví dụ về độ ưu tiên:
#include <iostream>
int main() {
using namespace std;
int x = 10, y = 5, z = 2;
int kq1 = x + y * z;
cout << "x + y * z = " << kq1 << endl;
int kq2 = (x + y) * z;
cout << "(x + y) * z = " << kq2 << endl;
return 0;
}
x + y * z = 20
(x + y) * z = 30
Giải thích:
- Trong ví dụ đầu tiên, phép nhân
y * z
(kết quả 10) được thực hiện trước, sau đó mới cộng vớix
(10 + 10 = 20). - Trong ví dụ thứ hai, nhờ có dấu ngoặc đơn, phép cộng
x + y
(kết quả 15) được thực hiện trước, sau đó mới nhân vớiz
(15 * 2 = 30).
Bài tập ví dụ: C++ Bài 1.A3: Vẽ chữ nhật toàn số
Viết chương trình để nhập một số có một chữ số và in nó dưới dạng hình chữ nhật gồm 4 cột và 6 hàng.
INPUT FORMAT
Dòng đầu tiên chứa một số có một chữ số.
OUTPUT FORMAT
Hình chữ nhật gồm 4 cột và 6 hàng tương ứng.
Ví dụ 1:
Input
5
Ouput
5555
5 5
5 5
5 5
5 5
5555
Giải thích ví dụ mẫu:
- Nhập số 5, in ra hình chữ nhật 4 cột và 6 hàng theo cấu trúc với số 5.
Dưới đây là các bước gợi ý để giải bài này:
- Khai báo thư viện cần thiết: Bạn cần sử dụng thư viện để đọc dữ liệu từ bàn phím và in dữ liệu ra màn hình. Hãy tìm thư viện chuẩn của C++ cho mục đích này.
- Đọc dữ liệu: Đọc số nguyên có một chữ số từ đầu vào chuẩn. Lưu giá trị này vào một biến kiểu số nguyên.
- Xử lý giá trị đọc được: Số bạn đọc là kiểu số nguyên (ví dụ
int
). Để in nó ra màn hình dưới dạng ký tự (ví dụ số 5 thành ký tự '5'), bạn cần chuyển đổi giá trị số nguyên đó thành ký tự tương ứng. Có một cách đơn giản để làm điều này trong C++ là cộng giá trị số nguyên với ký tự '0'. - Cấu trúc in ấn: Bạn cần in ra một lưới 6 hàng và 4 cột. Cách phổ biến nhất để in ra một lưới có kích thước cố định là sử dụng các vòng lặp lồng nhau:
- Vòng lặp ngoài để duyệt qua từng hàng (từ hàng 0 đến hàng 5, tổng cộng 6 hàng).
- Vòng lặp trong để duyệt qua từng cột trong mỗi hàng (từ cột 0 đến cột 3, tổng cộng 4 cột).
- Logic in ký tự: Bên trong vòng lặp lồng nhau, tại mỗi vị trí (hàng
row
, cộtcol
), bạn cần quyết định in ký tự gì:- Nếu vị trí đó nằm ở biên của hình chữ nhật (hàng đầu tiên, hàng cuối cùng, cột đầu tiên hoặc cột cuối cùng), bạn in ký tự số đã chuyển đổi ở bước 3.
- Ngược lại (nếu vị trí đó nằm bên trong hình chữ nhật), bạn in một ký tự khoảng trắng (' ').
- Điều kiện để kiểm tra vị trí biên sẽ dựa vào chỉ số của hàng và cột. Với 6 hàng (chỉ số từ 0 đến 5) và 4 cột (chỉ số từ 0 đến 3), vị trí biên là khi
row == 0
hoặcrow == 5
hoặccol == 0
hoặccol == 3
.
- Kết thúc mỗi hàng: Sau khi vòng lặp cột kết thúc cho một hàng, bạn cần in một ký tự xuống dòng để chuyển sang in hàng tiếp theo.
Hãy thử viết mã dựa trên các bước này, sử dụng các cấu trúc lệnh for
, if-else
và các hàm nhập xuất chuẩn của C++ (cin
, cout
).
Chúc bạn thành công!
#include <iostream>
int main() {
using namespace std;
int n;
cin >> n;
char k = n + '0';
for (int i = 0; i < 6; ++i) {
for (int j = 0; j < 4; ++j) {
if (i == 0 || i == 5 || j == 0 || j == 3) {
cout << k;
} else {
cout << ' ';
}
}
cout << endl;
}
return 0;
}
5555
5 5
5 5
5 5
5 5
5555
Comments