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() {
int a = 20;
int b = 7;
int sum, difference, product, quotient, remainder;
sum = a + b; // Cộng: 20 + 7 = 27
difference = a - b; // Trừ: 20 - 7 = 13
product = a * b; // Nhân: 20 * 7 = 140
quotient = a / b; // Chia: 20 / 7 = 2 (chia số nguyên)
remainder = a % b; // Chia lấy dư: 20 % 7 = 6
cout << "Tổng: " << sum << endl;
cout << "Hiệu: " << difference << endl;
cout << "Tích: " << product << endl;
cout << "Thương (số nguyên): " << quotient << endl;
cout << "Phần dư: " << remainder << endl;
// Lưu ý về chia số thực
double c = 20.0;
double d = 7.0;
double realQuotient = c / d; // Chia số thực: 20.0 / 7.0 = 2.857...
cout << "Thương (số thực): " << realQuotient << endl;
return 0;
}
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
.
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() {
int count = 10;
count += 5; // count = count + 5; -> count = 15
cout << "Sau khi count += 5: " << count << endl;
count *= 2; // count = count * 2; -> count = 30
cout << "Sau khi count *= 2: " << count << endl;
return 0;
}
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() {
int i = 5;
int j = 5;
cout << "Giá trị ban đầu của i: " << i << endl; // Kết quả: 5
// Sử dụng i++ trong cout. Giá trị 5 được sử dụng TRƯỚC khi i tăng lên 6.
cout << "i++ (postfix increment) trong cout: " << i++ << endl; // Kết quả: 5
cout << "Giá trị của i sau i++: " << i << endl; // Kết quả: 6
cout << "\nGiá trị ban đầu của j: " << j << endl; // Kết quả: 5
// Sử dụng ++j trong cout. j tăng lên 6 TRƯỚC khi giá trị 6 được sử dụng.
cout << "++j (prefix increment) trong cout: " << ++j << endl; // Kết quả: 6
cout << "Giá trị của j sau ++j: " << j << endl; // Kết quả: 6
// Ví dụ với phép gán
int a = 10;
int b = ++a; // a tăng lên 11, sau đó 11 được gán cho b.
cout << "\nKhi b = ++a: a = " << a << ", b = " << b << endl; // Kết quả: a = 11, b = 11
int x = 10;
int y = x++; // Giá trị 10 của x được gán cho y, sau đó x tăng lên 11.
cout << "Khi y = x++: x = " << x << ", y = " << y << endl; // Kết quả: x = 11, y = 10
return 0;
}
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() {
int num1 = 15;
int num2 = 10;
cout << "num1 == num2: " << (num1 == num2) << endl; // 15 == 10 -> false (0)
cout << "num1 != num2: " << (num1 != num2) << endl; // 15 != 10 -> true (1)
cout << "num1 > num2: " << (num1 > num2) << endl; // 15 > 10 -> true (1)
cout << "num1 < num2: " << (num1 < num2) << endl; // 15 < 10 -> false (0)
cout << "num1 >= num2: " << (num1 >= num2) << endl; // 15 >= 10 -> true (1)
cout << "num1 <= num2: " << (num1 <= num2) << endl; // 15 <= 10 -> false (0)
bool isGreater = (num1 > num2); // Kết quả của (num1 > num2) là true
cout << "isGreater: " << isGreater << endl; // Kết quả: 1 (true)
int num3 = 15;
cout << "num1 == num3: " << (num1 == num3) << endl; // 15 == 15 -> true (1)
return 0;
}
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() {
int age = 22;
double gpa = 3.5;
bool hasJob = false;
// Sử dụng toán tử logic để kết hợp các điều kiện
// && (AND): Yêu cầu tuổi lớn hơn 18 VÀ GPA cao hơn 3.0
bool condition1 = (age > 18) && (gpa > 3.0);
// (22 > 18) && (3.5 > 3.0) -> true && true -> true
cout << "(age > 18) && (gpa > 3.0): " << condition1 << endl; // Kết quả: 1 (true)
// age > 25 && hasJob
// (22 > 25) && (false) -> false && false -> false
bool condition2 = (age > 25) && hasJob;
cout << "(age > 25) && hasJob: " << condition2 << endl; // Kết quả: 0 (false)
// || (OR): Yêu cầu tuổi nhỏ hơn 20 HOẶC có việc làm
bool condition3 = (age < 20) || hasJob;
// (22 < 20) || (false) -> false || false -> false
cout << "(age < 20) || hasJob: " << condition3 << endl; // Kết quả: 0 (false)
// age > 20 || gpa < 3.0
// (22 > 20) || (3.5 < 3.0) -> true || false -> true
bool condition4 = (age > 20) || (gpa < 3.0);
cout << "(age > 20) || (gpa < 3.0): " << condition4 << endl; // Kết quả: 1 (true)
// ! (NOT): Phủ định điều kiện hasJob
bool condition5 = !hasJob; // !false -> true
cout << "!hasJob: " << condition5 << endl; // Kết quả: 1 (true)
// ! (age > 30)
// !(false) -> true
bool condition6 = !(age > 30);
cout << "!(age > 30): " << condition6 << endl; // Kết quả: 1 (true)
// Kết hợp nhiều toán tử
// (age > 18 && gpa > 3.0) || !hasJob
// (true && true) || true -> true || true -> true
bool complexCondition = ((age > 18) && (gpa > 3.0)) || !hasJob;
cout << "((age > 18) && (gpa > 3.0)) || !hasJob: " << complexCondition << endl; // Kết quả: 1 (true)
return 0;
}
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() {
int x = 10, y = 5, z = 2;
// Biểu thức: x + y * z
// Theo quy tắc ưu tiên, phép nhân (*) làm trước phép cộng (+)
int result1 = x + y * z; // Tương đương: x + (y * z) -> 10 + (5 * 2) -> 10 + 10 = 20
cout << "x + y * z = " << result1 << endl;
// Sử dụng dấu ngoặc đơn để thay đổi thứ tự hoặc làm rõ
// Biểu thức: (x + y) * z
// Dấu ngoặc đơn ưu tiên hơn phép nhân
int result2 = (x + y) * z; // (10 + 5) * 2 -> 15 * 2 = 30
cout << "(x + y) * z = " << result2 << endl;
return 0;
}
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.
<br>
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!
Comments