Phép toán
Toán tử số học
| Toán tử | Chức năng |
|---|---|
+ (đơn ngôi) |
Dương |
- (đơn ngôi) |
Âm |
* (nhị phân) |
Nhân |
/ |
Chia |
% |
Lấy dư |
+ (nhị phân) |
Cộng |
- (nhị phân) |
Trừ |
Toán tử đơn ngôi và nhị phân
Toán tử đơn ngôi (còn gọi là toán tử một ngôi) là toán tử chỉ có một toán hạng, còn toán tử nhị phân (còn gọi là toán tử hai ngôi) có hai toán hạng. Ví dụ trong 1 + 2 thì dấu cộng là toán tử nhị phân, có hai toán hạng 1 và 2. Ngoài ra trong C++ còn có duy nhất một toán tử ba ngôi là ?:.
Trong các toán tử số học có hai toán tử đơn ngôi (dương, âm) và năm toán tử nhị phân (nhân, chia, lấy dư, cộng, trừ), trong đó toán tử đơn ngôi có độ ưu tiên cao nhất.
Toán tử lấy dư % dùng để lấy phần dư khi chia hai số nguyên.
Còn - khi là toán tử nhị phân thì là phép trừ, như 2-1; khi là toán tử đơn ngôi thì là lấy đối (âm), như -1.
Cách dùng như sau:
op=x-y*z
Giá trị của op tuân theo quy tắc ưu tiên của cộng trừ nhân chia trong toán học: thực hiện phép có độ ưu tiên cao trước, cùng độ ưu tiên thì theo tính kết hợp, ngoặc sẽ nâng độ ưu tiên.
Chuyển đổi kiểu trong phép toán số học
Với toán tử số học nhị phân, khi hai biến tham gia có cùng kiểu thì không xảy ra chuyển đổi kiểu, kết quả sẽ dùng kiểu đó để biểu diễn; nếu khác kiểu thì sẽ xảy ra chuyển đổi kiểu để hai biến có cùng kiểu. Quy tắc chuyển đổi xem tại chuyển đổi kiểu.
Ví dụ, với một biến số nguyên (int) \(x\) và một biến số thực kép (double) \(y\):
x/3cho kết quả kiểu số nguyên;x/3.0cho kết quả kiểu số thực kép;x/ycho kết quả kiểu số thực kép;x*1/3cho kết quả kiểu số nguyên;x*1.0/3cho kết quả kiểu số thực kép;
Toán tử bit
Xem thêm: Phép toán bit.
| Toán tử | Chức năng |
|---|---|
~ |
NOT theo bit |
& (nhị phân) |
AND theo bit |
| |
OR theo bit |
^ |
XOR theo bit |
<< |
Dịch trái theo bit |
>> |
Dịch phải theo bit |
Ý nghĩa các phép toán bit xem tại phép toán bit. Cần lưu ý độ ưu tiên của phép toán bit thấp hơn toán tử số học (trừ phép NOT), và AND/OR/XOR theo bit còn thấp hơn toán tử so sánh (xem Bảng độ ưu tiên toán tử C++), nên khi dùng cần chú ý, khi cần thì thêm ngoặc.
Trong phép dịch bit, nếu xuất hiện các tình huống sau thì hành vi là không xác định:
- Toán hạng bên phải (số bit dịch) là số âm;
- Toán hạng bên phải lớn hơn hoặc bằng số bit của toán hạng bên trái;
Ví dụ với biến a kiểu int32_t, a<<-1 và a<<32 đều là không xác định.
Với phép dịch trái trên số có dấu không âm, cần đảm bảo kết quả sau dịch có thể biểu diễn trong kiểu của số ban đầu, nếu không thì cũng là không xác định.1 Dịch trái một số âm cũng là không xác định.2
Với phép dịch phải, các bit thừa bên phải sẽ bị bỏ, còn bên trái phức tạp hơn: với số không dấu, sẽ điền \(0\)3; còn với số có dấu thì điền bit cao nhất (tức bit dấu, số không âm là \(0\), số âm là \(1\))4.
Toán tử tăng/giảm
Đôi khi ta cần tăng biến lên 1 (tăng) hoặc giảm đi 1 (giảm), lúc này toán tử tăng ++ và giảm -- sẽ hữu ích.
Toán tử tăng/giảm có thể đặt trước hoặc sau biến, trước là tiền tố, sau là hậu tố. Khi dùng riêng lẻ thì tiền/hậu tố không cần phân biệt; nếu dùng giá trị của biểu thức thì cần chú ý, xem ví dụ dưới đây. Chi tiết xem phần ví dụ trong tham chiếu.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
Toán tử gán kết hợp
Toán tử gán kết hợp thực chất là dạng rút gọn của biểu thức. Có thể chia thành toán tử số học kết hợp +=、-=、*=、/=、%= và toán tử bit kết hợp &=、|=、^=、<<=、>>=.
Ví dụ, op = op + 2 có thể viết op += 2, op = op - 2 viết op -= 2, op= op * 2 viết op *= 2.
Toán tử điều kiện
Toán tử điều kiện có thể xem là dạng rút gọn của câu lệnh if, trong a ? b : c nếu biểu thức a đúng thì kết quả là b, ngược lại là c.
Toán tử so sánh
| Toán tử | Chức năng |
|---|---|
> |
Lớn hơn |
>= |
Lớn hơn hoặc bằng |
< |
Nhỏ hơn |
<= |
Nhỏ hơn hoặc bằng |
== |
Bằng |
!= |
Không bằng |
Đặc biệt cần phân biệt toán tử so sánh bằng == với toán tử gán =; điều này rất quan trọng trong các câu lệnh điều kiện.
if (op=1) và if (op==1) nhìn có vẻ giống nhau, nhưng chức năng khác hẳn. Câu lệnh đầu là gán giá trị cho op, nếu gán khác 0 thì là true, điều kiện luôn đúng, không còn tác dụng kiểm tra; còn câu lệnh thứ hai mới là so sánh giá trị của op.
Toán tử logic
| Toán tử | Chức năng |
|---|---|
&& |
AND logic |
|| |
OR logic |
! |
NOT logic |
1 2 3 4 5 | |
Các toán tử built-in && và || có đánh giá ngắn mạch (nếu sau khi đánh giá toán hạng đầu tiên đã biết kết quả thì không đánh giá toán hạng thứ hai), còn toán tử đã bị nạp chồng thì không có đặc tính này và luôn đánh giá cả hai toán hạng.
Toán tử dấu phẩy
Toán tử dấu phẩy dùng để phân tách nhiều biểu thức, các biểu thức được tính từ trái sang phải, giá trị của toàn biểu thức là giá trị của biểu thức cuối. Độ ưu tiên của toán tử dấu phẩy là thấp nhất trong tất cả toán tử.
1 2 3 4 5 6 7 8 9 10 | |
Toán tử truy cập thành viên
| Toán tử | Chức năng |
|---|---|
[] |
Chỉ số mảng |
. |
Thành viên của đối tượng |
& (đơn ngôi) |
Lấy địa chỉ/lấy tham chiếu |
* (đơn ngôi) |
Truy cập gián tiếp/giải tham chiếu |
-> |
Thành viên qua con trỏ |
Các toán tử này dùng để truy cập thành viên của đối tượng hoặc bộ nhớ; ngoài toán tử cuối cùng, các toán tử trên đều có thể nạp chồng. Nội dung liên quan đến &、* và -> xem con trỏ và tham chiếu. Ở đây bỏ qua hai toán tử ít dùng .* và ->*, cách dùng xem C++ language manual.
1 2 3 4 5 | |
Bảng tổng hợp độ ưu tiên toán tử C++
Trích từ C++ operator precedence - cppreference, có chỉnh sửa.
| Toán tử | Mô tả | Ví dụ | Khả năng nạp chồng |
|---|---|---|---|
| Mức 1 | |||
:: |
Toán tử phân giải phạm vi | Class::age = 2; |
Không nạp chồng |
| Mức 2 | |||
++ |
Hậu tố tăng | for (int i = 0; i < 10; i++) cout << i; |
Có thể nạp chồng |
-- |
Hậu tố giảm | for (int i = 10; i > 0; i--) cout << i; |
Có thể nạp chồng |
type() type{} |
Ép kiểu | unsigned int a = unsigned(3.14); |
Có thể nạp chồng |
() |
Gọi hàm | isdigit('1') |
Có thể nạp chồng |
[] |
Truy cập mảng | array[4] = 2; |
Có thể nạp chồng |
. |
Truy cập thành viên đối tượng | obj.age = 34; |
Không nạp chồng |
-> |
Truy cập thành viên qua con trỏ | ptr->age = 34; |
Có thể nạp chồng |
| Mức 3 (kết hợp từ phải sang trái) | |||
++ |
Tiền tố tăng | for (i = 0; i < 10; ++i) cout << i; |
Có thể nạp chồng |
-- |
Tiền tố giảm | for (i = 10; i > 0; --i) cout << i; |
Có thể nạp chồng |
+ |
Dấu dương | int i = +1; |
Có thể nạp chồng |
- |
Dấu âm | int i = -1; |
Có thể nạp chồng |
! |
Phủ định logic | if (!done) … |
Có thể nạp chồng |
~ |
Phủ định theo bit | flags = ~flags; |
Có thể nạp chồng |
(type) |
Ép kiểu C | int i = (int) floatNum; |
Có thể nạp chồng |
* |
Giải tham chiếu | int data = *intPtr; |
Có thể nạp chồng |
& |
Lấy địa chỉ | int *intPtr = &data; |
Có thể nạp chồng |
sizeof |
Kích thước bộ nhớ | int size = sizeof floatNum; int size = sizeof(float); |
Không nạp chồng |
new |
Cấp phát bộ nhớ động | long *pVar = new long; MyClass *ptr = new MyClass(args); |
Có thể nạp chồng |
new [] |
Cấp phát mảng động | long *array = new long[n]; |
Có thể nạp chồng |
delete |
Giải phóng phần tử động | delete pVar; |
Có thể nạp chồng |
delete [] |
Giải phóng mảng động | delete [] array; |
Có thể nạp chồng |
| Mức 4 | |||
.* |
Truy cập thành viên qua con trỏ thành viên (đối tượng) | obj.*var = 24; |
Không nạp chồng |
->* |
Truy cập thành viên qua con trỏ thành viên (con trỏ) | ptr->*var = 24; |
Có thể nạp chồng |
| Mức 5 | |||
* |
Nhân | int i = 2 * 4; |
Có thể nạp chồng |
/ |
Chia | float f = 10.0 / 3.0; |
Có thể nạp chồng |
% |
Lấy dư (mod) | int rem = 4 % 3; |
Có thể nạp chồng |
| Mức 6 | |||
+ |
Cộng | int i = 2 + 3; |
Có thể nạp chồng |
- |
Trừ | int i = 5 - 1; |
Có thể nạp chồng |
| Mức 7 | |||
<< |
Dịch trái theo bit | int flags = 33 << 1; |
Có thể nạp chồng |
>> |
Dịch phải theo bit | int flags = 33 >> 1; |
Có thể nạp chồng |
| Mức 8 | |||
<=> |
Toán tử so sánh ba chiều | if ((i <=> 42) < 0) ... |
Có thể nạp chồng |
| Mức 9 | |||
< |
Nhỏ hơn | if (i < 42) ... |
Có thể nạp chồng |
<= |
Nhỏ hơn hoặc bằng | if (i <= 42) ... |
Có thể nạp chồng |
> |
Lớn hơn | if (i > 42) ... |
Có thể nạp chồng |
>= |
Lớn hơn hoặc bằng | if (i >= 42) ... |
Có thể nạp chồng |
| Mức 10 | |||
== |
Bằng | if (i == 42) ... |
Có thể nạp chồng |
!= |
Không bằng | if (i != 42) ... |
Có thể nạp chồng |
| Mức 11 | |||
& |
AND theo bit | flags = flags & 42; |
Có thể nạp chồng |
| Mức 12 | |||
^ |
XOR theo bit | flags = flags ^ 42; |
Có thể nạp chồng |
| Mức 13 | |||
| |
OR theo bit | flags = flags | 42; |
Có thể nạp chồng |
| Mức 14 | |||
&& |
AND logic | if (conditionA && conditionB) ... |
Có thể nạp chồng |
| Mức 15 | |||
|| |
OR logic | if (conditionA || conditionB) ... |
Có thể nạp chồng |
| Mức 16 (kết hợp từ phải sang trái) | |||
? : |
Toán tử điều kiện | int i = a > b ? a : b; |
Không nạp chồng |
throw |
Ném ngoại lệ | throw EClass("Message"); |
Không nạp chồng |
= |
Gán | int a = b; |
Có thể nạp chồng |
+= |
Cộng gán | a += 3; |
Có thể nạp chồng |
-= |
Trừ gán | b -= 4; |
Có thể nạp chồng |
*= |
Nhân gán | a *= 5; |
Có thể nạp chồng |
/= |
Chia gán | a /= 2; |
Có thể nạp chồng |
%= |
Lấy dư gán | a %= 3; |
Có thể nạp chồng |
<<= |
Dịch trái gán | flags <<= 2; |
Có thể nạp chồng |
>>= |
Dịch phải gán | flags >>= 2; |
Có thể nạp chồng |
&= |
AND bit gán | flags &= new_flags; |
Có thể nạp chồng |
^= |
XOR bit gán | flags ^= new_flags; |
Có thể nạp chồng |
|= |
OR bit gán | flags |= new_flags; |
Có thể nạp chồng |
| Mức 17 | |||
, |
Dấu phẩy | for (i = 0, j = 0; i < 10; i++, j++) ... |
Có thể nạp chồng |
Lưu ý bảng không liệt kê các toán tử const_cast, static_cast, dynamic_cast, reinterpret_cast, typeid, sizeof..., noexcept và alignof vì dạng dùng của chúng giống lời gọi hàm nên không gây nhập nhằng.
Tài liệu tham khảo và chú thích
-
Trước C++20, nếu giá trị ban đầu là kiểu có dấu và kết quả sau dịch có thể biểu diễn bằng phiên bản không dấu của kiểu đó, thì kết quả sẽ được chuyển đổi sang kiểu có dấu tương ứng, nếu không thì hành vi không xác định; với số không dấu, dịch trái sẽ bỏ các bit tràn khỏi kiểu kết quả. Từ C++20, quy định
a << blà \(a\cdot 2^b\) theo modulo \(2^N\) (với \(N\) là độ rộng bit của kiểu kết quả), tức dù có dấu hay không dấu, dịch trái đều bỏ các bit tràn (tức dịch trái số học/logic). ↩↩ -
Tức dịch phải logic. ↩
-
Tức dịch phải số học. Trước C++20, dịch phải số có dấu là phụ thuộc hiện thực, đa số hiện thực dùng dịch phải số học. Từ C++20, quy định
a >> blà \(\lfloor a/2^b\rfloor\), nên dịch phải số có dấu là dịch phải số học. ↩
Last updated on this page:, Update history
Found an error? Want to help improve? Edit this page on GitHub!
Contributors to this page:aofall, greyqz, Ir1d, Link-cute, Marcythm, ouuan, Shen-Linwood, sshwy, StudyingFather
All content on this page is provided under the terms of the CC BY-SA 4.0 and SATA license, additional terms may apply