Chương 2: Cú pháp lập trình
Mở đầu về lập trình với cú pháp, thao tác cơ bản với dữ liệu trong C++
1. Cú pháp cơ bản
1.1. Cấu trúc chương trình C++
Cấu trúc của một đoạn mã tương ứng với một chương trình máy tính thường gồm các thành phần sau:
| Thành phần | Ý nghĩa |
|---|---|
#Tiền xử lý | Khai báo thư viện và định nghĩa các marco. |
| Khai báo biến, hàm (prototype function) | Khai báo các biến toàn cục và các hàm được sử dụng trong chương trình chính. |
int main() { } | Chương trình chính. Nội dung của hàm, thực hiện các chức năng chính của chương trình. |
| Định nghĩa hàm (define function) | Định nghĩa thân hàm của các hàm đã được khai báo trong phần trước đó. Lưu ý: Nếu một hàm f đã được khai báo và có lời gọi hàm đến f thì bắt buộc phải có định nghĩa hàm của f. |
Ví dụ: Xét chương trình tính diện tích hình tròn
// Tinh dien tich hinh tron
#include <iostream>
using namespace
#define PI 3.14
float TinhDienTich (int);
int main(){
float r;
cin >> r;
cout<< TinhDienTich (int r);
return r*r*PI;
}
Dòng lệnh 1: Dòng ghi chú.
Dòng lệnh 2: Khai báo cho biết trong chương trình có sử dụng các hàm hoặc biến (đối tượng) được khai báo và cài đặt trước trong thư việniostream.
Dòng lệnh 3: Khai báonamespace std(để cùngcout,cin).
Dòng lệnh 4: Định nghĩa marcoPIvới giá trị3.14
Dòng lệnh 5: Nguyên mẫu hàmTinhDienTich.
Dòng lệnh 6: Khai báo hàm chính của chương trình, đây là hàm bắt buộc phải có để chương trình có thể thực thi được.
Dòng lệnh 7: Khai báo biếnrcó kiểu dữ liệufloat.
Dòng lệnh 8: Nhập giá trịr.
Dòng lệnh 9: Gọi hàmTinhDienTichvà in ra kết quả.
Dòng lệnh 10: Lệnhreturncó tác dụng kết thúc hàmmain.
Dòng lệnh 11: Là ký hiệu cho biết kết thúc nội dung của hàmmain.
Dòng lệnh 12, 13, 14: Định nghĩa hàmTinhDienTich.
1.2. Bộ keywords trong C++
1.2.1. Ký tự (Character)
C++ sử dụng nhiều loại ký tự khác nhau để viết chương trình:
- Chữ cái ký tự Latin:
A,B,C, ...,Z,a,b,c, ...,z - Chữ số thập phân:
0,1,2,3,4,5,6,7,8,9 - Chú thích
- Chú thích một dòng:
// - Chú thích nhiều dòng:
/*...*/
- Chú thích một dòng:
- Dấu nháy đơn: Ký tự được đặt trong dấu nháy đơn
'...' - Dấu nháy kép: Chuỗi được đặt trong dấu nháy kép
"..." - Ký tự điều khiển: sử dụng ký tự thoát (escape characters) là dấu gạch chéo ngược
\đặt ở đầu:\n(dòng mới),\t(tab ngang),\\(ký tự gạch chéo ngược),\'(dấu nháy đơn),\"(dấu nháy kép),\0(null) - Toán tử:
+,-,*,/,%,++,--,=,+=,-=,*=,/=,%=,==,!=,>,<,>=,<=,&&,||,!,?,=(toán tử điều kiện) - Ký tự phân tách:
;(dấu chấm phẩy),,(dấu phẩy) - Ký tự ngoặc:
(,),{,},[,] - Ký tự toán tử bit:
&,|,^,~,<<,>> - Ký tự điều khiển:
#(cho các chỉ thị tiền xử lý) - Ký tự chấm:
.(truy cập thành viên) - Ký tự mũi tên:
->(truy cập thành viên qua con trỏ) - Ký tự hai chấm:
:(sử dụng trong toán tử ba ngôi và khai báo phạm vi),::(toán tử phạm vi) - Ký tự dấu gạch dưới:
_(thường được sử dụng trong tên biến hoặc hàm)
1.2.2. Từ khóa (keyword)
Không thể sử dụng từ khóa để đặt tên cho biến, hàm, tên chương trình con.
Keyword chỉ chứa ký tự chữ cái thường.
Không có ký hiệu hoặc dấu câu đặc biệt nào được sử dụng trong từ khóa.
Một số từ khóa thông dụng:
- Nhóm 1: Điều khiển luồng
if,else,switch,case,default,for,while,do,break,continue,goto,return,try,catch,throw - Nhóm 2: Khai báo và định nghĩa
int,char,float,double,void,bool,short,long,signed,unsigned,enumclass,struct,union,typedef,constexpr,consteval,concept,decltype,inline,static,extern,mutable,thread_local,register
- Nhóm 3: Truy cập và quản lý bộ nhớ
new,delete,sizeof,alignas,alignof,reinterpret_cast,static_cast,dynamic_cast,const_cast - Nhóm 4: Lập trình hướng đối tượng
class,public,private,protected,virtual,explicit,friend,this,operator - Nhóm 5: Không gian tên và mô-đun
namespace,using,export,module,import - Nhóm 6: Từ khóa không đồng bộ và đồng thời
co_await,co_return,co_yield,synchronized,atomic_cancel,atomic_commit,atomic_noexcept - Nhóm 7: Logic và toán tử
and,or,not,bitand,bitor,compl,and_eq,or_eq,not_eq - Nhóm 8: Các từ khóa khác
asm,auto,bool,false,true,nullptr,static_assert,noexcept,requires,reflexpr - Nhóm 9: Các kiểu dữ liệu và các từ khóa liên quan đến kiểu
int,char,char8_t,char16_t,char32_t,float,double,void,bool
1.2.3. Dấu chấm phẩy
Dùng để phân cách các câu lệnh.
Các câu lệnh có thể viết trên 1 dòng, tuy nhiên luôn được phân cách bằng dấu chấm phẩy ;
cout << "Xin chao"; return 0;
1.2.4. Tên/ Định danh (Name/Indetifier)
Một dãy ký tự dùng chỉ tên hằng, biến, hàm.
Được tạo thành từ các chữ cái, chữ số, dấu gạch nối _
Quy ước đặt tên:
- Không trùng với các từ khóa
- Ký tự đầu tiên là chữ cái hoặc
_ - Tối đa 255 ký tự
- Không được sử dụng khoảng trắng ở giữa các ký tự
- Tên có phân biệt chữ hoa chữ thường (case sensitive).
1.2.5. Câu chú thích
Dùng để mô tả hoặc ghi chú trong source code, giúp dễ dàng đọc code sau này
Có 2 cách để viết chú thích trong C++
- Cách 1: Viết chú thích trong /chú thích/
/* Day la chu thich 1
Day la chu thich 2 */ - Cách 2: Viết chú thích sau // chú thích
// Day la chu thich 1
// Day la chu thich 2
Nên sử dụng nhất quán 1 cách. Cách 2 được sử dụng phổ biến hơn
Khi biên dịch source code thì các phần comments sẽ không được biên dịch
2. Dữ liệu trong lập trình
Về bản chất, lập trình là tập hợp các thao tác điều khiển và xử lý dữ liệu để giải quyết một bài toán cụ thể. Để một chương trình hoạt động hiệu quả, chúng ta không chỉ cần các dòng lệnh logic mà còn phải quản lý tốt "nguyên liệu" đầu vào và đầu ra.
Trong phần này, ta sẽ tìm hiểu các kiểu dữ liệu để phân loại dữ liệu, cách lưu trữ dữ liệu (biến) và các phép toán biến đối dữ liệu.
2.1. Kiểu dữ liệu
2.1.1. Khái niệm kiểu dữ liệu
Kiểu dữ liệu (data type) là kiểu hay loại của dữ liệu.
Ví dụ: giá trị dữ liệu 3.2 có kiểu là số thực và giá trị 5 có kiểu là số nguyên.
Để thuận tiện cho việc đọc và ghi các giá trị dữ liệu, các trình biên dịch thường quy ước mỗi kiểu dữ liệu sẽ chiếm một không gian lưu trữ nhất định trong bộ nhớ, nghĩa là cần một số bits cố định cho mỗi kiểu dữ liệu.
Lưu ý:
- Để lưu trữ số nguyên có giá trị
0và số nguyên có giá trị1000thì đều cần một số lượng bit như nhau vì chúng đều có kiểu là số nguyên. - Cùng một dữ liệu nhưng trên các trình biên dịch khác nhau có thể khác nhau.
Đặc điểm của kiểu dữ liệu: Các kiểu dữ liệu thường có 2 đặc điểm chính:
- Bao gồm một tập hợp các giá trị mà biến thuộc kiểu đó có thể nhận
- Các phép toán có thể thực hiện
Các kiểu dữ liệu trong ngôn ngữ lập trình:
- Các kiểu được cung cấp bởi thư viện có sẵn
- Các kiểu do lập trình viên định nghĩa (user defined types)
2.1.2 Kiểu dữ liệu số nguyên
Kiểu dữ liệu số nguyên được dùng để biểu diễn các giá trị nguyên (integer). Ký hiệu, kích thước và miền giá trị một số kiểu dữ liệu số nguyên được mô tả trong bảng sau:
| Kiểu dữ liệu | Kích thước lưu trữ (bytes) | Miền giá trị (Value range) |
|---|---|---|
signed char | 1 | -128 → 127 |
signed short int | 2 | -32,768 → 32,767 |
signed int | 4 | -2,147,483,648 → 2,147,483,647 |
signed long | 4 | -2,147,483,648 → 2,147,483,647 |
signed long long int | 8 | -9,223,372,036,854,775,808 → 9,223,372,036,854,775,807 |
unsigned char | 1 | 0 → 255 |
unsigned short int | 2 | 0 → 65,535 |
unsigned int | 4 | 0 → 4,294,967,295 |
unsigned long int | 4 | 0 → 4,294,967,295 |
unsigned long long int | 8 | 0 → 18,446,744,073,709,551,615 |
- Phạm vi các kiểu số nguyên bit có dấu: .
- Phạm vi các kiểu số nguyên bit không dấu:
2.1.3 Kiểu dữ liệu số thực
Kiểu dữ liệu số thực được dùng để biểu diễn các giá trị thực (real). Ký hiệu, kích thước và miền giá trị một số kiểu số thực được mô tả trong bảng sau:
| Kiểu dữ liệu | Kích thước lưu trữ (bytes) | Miền giá trị (Value range) | Độ chính xác (precision) |
|---|---|---|---|
float | 4 | [1.17549E−38, 3.50282E+38] | ~6–7 chữ số |
double | 8 | [2.22507E−308, 1.79769E+308] | ~15–16 chữ số |
long double | 12 | [3.4621E−4932, 1.1893E+4932] | ~18–19 chữ số |
2.1.4 Kiểu luận lý/logic
Kiểu dữ liệu bool (Boolean) trong C++ được sử dụng để biểu diễn các giá trị logic, thường được gọi là "đúng" (true) hoặc "sai" (false).
| Kiểu dữ liệu | Kích thước | Giá trị |
|---|---|---|
bool | 1 byte | - false: giá trị 0- true: giá trị khác 0 |
2.1.5 Kiểu dữ liệu void
Kiểu dữ liệu void là một kiểu dữ liệu đặc biệt vì nó được dùng để biểu diễn cho dữ liệu không có giá trị cụ thể.
| Kiểu dữ liệu | Kích thước lưu trữ (bytes) | Miền giá trị (Value range) |
|---|---|---|
void | 1 |
Kiểu dữ liệu void thường được sử dụng trong trường hợp sau:
- Một hàm số mà nó không trả về giá trị kết quả nào cả.
- Một hàm số mà tham số của nó không chấp nhận giá trị nào.
- Con trỏ dùng để lưu trữ địa chỉ của một vùng nhớ mà kiểu dữ liệu của vùng nhớ này chưa xác định.
Lưu ý:
- Kích thước các kiểu dữ liệu có thể khác nhau tùy theo trình biên dịch.
- Để biết kích thước của một kiểu dữ liệu (hoặc một biến), ta có thể sử dụng toán tử
sizeof(tên_kiểu_dữ_liệu).
2.2. Biến
2.2.1. Khái niệm biến
Biến là một khái niệm dùng để chỉ một vùng bộ nhớ được cấp phát nhằm mục đích lưu trữ giá trị (giá trị này có thể do người dùng nhập vào hoặc giá trị của một biểu thức; giá trị này có thể thay đổi trong quá trình thực hiện chương trình hoặc trong các lần chương trình được thực thi khác nhau) và được truy xuất thông qua một tên đã được khai báo cho biến đó.
Mỗi biến sẽ có một kiểu dữ liệu tương ứng với kiểu của các giá trị dữ liệu sẽ được lưu trữ.
2.2.2. Khai báo biến
Cú pháp khai báo biến có dạng chung:
<Kiểu_dữ_liệu> tên_biến;
Trong đó:
<Kiểu_dữ_liệu>xác định kiểu của dữ liệu được lưu trữ, còn được gọi là kiểu dữ liệu của biến.Tên_biếnlà một định danh được gán cho một vùng nhớ tương ứng. Ta có thể truy xuất dữ liệu được lưu trữ tại vùng nhớ đó qua tên biến.- Dấu
;dùng để xác định sự kết thúc của câu lệnh khai báo biến.
Ta có thể thực hiện khai báo biến và khởi tạo giá trị cho biến theo cú pháp:
<Kiểu_dữ_liệu> tên_biến = giá_trị_khởi_tạo;
Lưu ý: Giá trị của biến chính là giá trị được lưu trữ trong vùng nhớ dành cho biến đó.
Trong một dòng lệnh có thể khai báo nhiều biến có cùng kiểu bằng cách phân tách các biến đó bởi dấu phẩy , theo cú pháp như sau:
<Kiểu_dữ_liệu> tên_biến_1, tên_biến_2, tên_biến_3;
Lưu ý:
- Luôn luôn khai báo biến trước khi sử dụng biến đó.
- Nếu một biến X được sử dụng nhưng chưa được khai báo trong các dòng lệnh trước đó thì trình biên dịch thường báo lỗi: "error: 'X' was not declared in this scope"
2.2.3. Quy tắc/ cách thức đặt tên biến
Khi đặt tên biến cần lưu ý những vấn đề sau:
- Không được sử dụng khoảng trắng (space) ở giữa các ký tự.
- Không bắt đầu bằng chữ số.
- Không trùng tên với các từ khóa hoặc tên hàm.
- Ký tự đầu tiên là các chữ cái hoặc
_. - Nên sử dụng tất cả chữ thường với dấu
_giữa các từ.
Ví dụ: Giả sử cần khai báo một biến để chứa giá trị diện tích của hình tròn. Khi đó:
- Khai báo
double Dien tich;là khai báo sai vì có khoảng trắng bên trong tên biến.- Khai báo
double Dientich;hoặcdouble Dien_tich;là khai báo đúng.
2.2.4. Phạm vi của biến
Mỗi biến sẽ có một phạm vi tồn tại và hoạt động trong chương trình. Dựa trên phạm vi hoạt động, người ta thường chia các biến thành biến toàn cục (global) và biến cục bộ (local).
- Biến toàn cục: nếu biến được khai báo ở bên ngoài khối lệnh (hoặc ở bên ngoài các hàm) thì biến đó được gọi là biến toàn cục so với khối lệnh (hoặc các hàm) đó. Thời gian tồn tại và phạm vi hoạt động của nó sẽ tính từ vị trí khai báo đến hết khối lệnh chứa biến (hoặc chương trình).
- Biến cục bộ: là biến được khai báo bên trong khối lệnh (hoặc bên trong phạm vi của hàm). Phạm vi sử dụng của biến là bên trong khối lệnh (hoặc trong phạm vi của hàm) mà nó được khai báo. Thời gian tôn tại của biến là thời gian bắt đầu chương trình sử dụng khối lệnh (hoặc hàm) đó đến khi kết thúc khối lệnh (hoặc hàm).
Lưu ý:
Tính toàn cục và cục bộ của các biến là tương đối, nghĩa là nếu biến z là biến cục bộ so với biến y thì biến y sẽ là biến toàn cục đối với z, tuy nhiên, biến y có thể là biến cục bộ so với biến x và biến x là biến toàn cục so với biến y.
Ví dụ:
...
int x;
{
int y;
{
int z;
}
}
...
Biến cục bộ có thể trùng tên với biến toàn cục. Khi đó, để truy xuất đến biến toàn cục ta sử dụng dấu 4 chấm :: để truy xuất.
Ví dụ:
//CODE // Giải thích
#include <iostream>
using namespace std;
int x = 9; // Biến x toàn cục
int main() {
cout << x; // In ra giá trị x là 9
int x = 1; // Biến x cục bộ trong hàm main
{
cout << x; // In ra giá trị x là 1
int x = 7; // Biến x cục bộ trong khối lệnh con
cout << x; // In ra giá trị x là 7
cout << ::x; // Sử dụng toán tử :: để in ra x toàn cục (9)
}
cout << x; // In ra giá trị x là 1 (x của hàm main)
cout << ::x; // In ra giá trị x là 9 (x toàn cục)
return 0;
}
2.2.5. Một số loại biến thường gặp
- Biến thông thường (normal): là biến đơn giản, được dùng để lưu giá trị thông thường.
- Biến con trỏ (pointer): là biến mà giá trị của nó là địa chỉ. Thông thường ta dùng biến này khi cần lưu trữ địa chỉ của một vùng nhớ.
- Biến tham chiếu (reference): là biến nhưng nó không được trình biên dịch cấp phát ô nhớ riêng và phải sử dụng chung ô nhớ với biến được tham chiếu.
2.3. Hằng
2.3.1. Khái niệm hằng
Hằng là các đại lượng mà giá trị của nó không thay đổi trong quá trình thực hiện chương trình hoặc giữa các lần chương trình được thực thi khác nhau.
2.3.2. Khai báo hằng
Cách 1: dùng chỉ thị tiền xử lý "preprocessor definition" theo cú pháp sau:
#define tên_hằng giá_trị_hằngLưu ý: Tên hằng thông thường được khai báo bằng chữ in hoa.
Cách 2: dùng biến hằng theo cú pháp sau:
const kiểu_dữ_liệu tên_biến = giá_trị_khởi_tạo ;
Ví dụ:
// --- Cách 1: Sử dụng tiền xử lý #define ---
#define PI 3.14
int main() {
double R = 0, S;
// Tính diện tích hình tròn
S = PI * R * R;
return 0;
}
// --- Cách 2: Sử dụng biến hằng const (Khuyên dùng trong C++) ---
const double PI = 3.14;
int main() {
double R = 0, S;
// Tính diện tích hình tròn
S = PI * R * R;
return 0;
}
2.4. Các phép toán
Lập trình là quá trình điều khiển dữ liệu thông qua các biểu thức. Một biểu thức biến đổi dữ liệu hoàn chỉnh thường bao gồm:
- Biến: Nơi lưu trữ dữ liệu cần xử lý.
- Giá trị: Các dữ liệu trực tiếp dùng trong tính toán (ví dụ:
1,9,3.14). - Toán tử (Operators): Công cụ thực hiện việc biến đổi dữ liệu. Kết quả của các toán tử là một dữ liệu mới và được gán vào biến.
Như vậy, quy trình xử lý dữ liệu thực chất là việc lấy giá trị từ biến, dùng toán tử để tính toán và gán lại kết quả mới vào biến để điều khiển dữ liệu. Trong phần này, ta sẽ tìm hiểu các loại toán tử phổ biến trong C++ và các ngôn ngữ lập trình khác.
2.4.1. Toán tử gán
Toán tử gán = cho phép gán trực tiếp một giá trị vào một biến, hoặc gán giá trị từ biến này sang biến.
tên_biến = giá_trị;
Ví dụ:
int a = 5 // Gán biến cho biến a giá trị 5
int b = a // Gán cho biến b giá trị của biến a (5)
2.4.2. Toán tử số học
| Ký hiệu phép toán | Phép toán |
|---|---|
+ | Cộng |
- | Trừ |
* | Nhân |
/ | Chia |
% | Lấy phần dư |
Các phép toán tử thực hiện phép toán theo khác quy luật số học trên kiểu số nguyên, số thực
Kiểu trả về của các toán tử trên là kiểu lớn nhất trong các giá trị tham gia nếu không thực hiện ép kiểu.
Ví dụ:
int a = 1, b = 2;
double c = 1.5;
int n = a / b; // n sẽ bằng 0, do kết quả trả về là một số nguyên nên 0.5 được chuyển về số nguyên là 0
int i = b / c; // i sẽ bằng 0, kết quả phép chia trả về là double mang giá trị 1.33333, tuy nhiên ta thực hiện ép kiểu double xuống int nên i còn giá trị 1
double d = b / c // d sẽ bằng 1.33333, do kiểu của d cùng với kiểu trả về của phép chia.
Toán tử chia lấy phần dư (%) chỉ được áp dụng trên các toán hạng có kiểu số nguyên. Nếu áp dụng trên kiểu dữ liệu khác thì trình biên dịch sẽ báo lỗi.
2.4.3. Phép toán tăng/ giảm một đơn vị
Toán tử tăng (++) tăng 1 đơn vị cho toán hạng của nó, toán tử giảm (--) trừ 1 đơn vị cho toán hạng của nó.
Các toán tử tăng, giảm chỉ áp dụng cho toán hạng là các biến dạng số hoặc con trỏ.
Ví dụ:
++x;tương đươngx += 1;tương đươngx = x+1;- Biểu thức
(i+j)++không hợp lệ
Giả sử x là biến (toán hạng) sử dụng toán tử ++, --, ta có các dạng sau:
++x: Đây được gọi là Prefix increment. Trong biểu thức chứa++x,xsẽ tăng lên 1 đơn vị trước tiên, sau đó giá trị cập nhật sẽ được sử dụng.x++: Đây được gọi là Postfix increment. Trong biểu thức chứax++, biếnxsử dụng giá trị hiện tại củax(chưa tăng lên 1 đơn vị), tính xong biểu thức rồi cuối cùng mới tăngxlên 1 đơn vị.--x: Đây được gọi là Prefix decrement. Trong biểu thức chứa--x, biếnxsẽ giảm xuống 1 đơn vị trước, sau đó giá trị cập nhật sẽ được sử dụng.x--: Đây được gọi là Postfix decrement. Trong biểu thức chứax--, biếnxsử dụng giá trị hiện tại củax(chưa giảm 1 đơn vị), tính xong biểu thức rồi cuối cùng mới giảmx1 đơn vị.
Ví dụ:
#include <iostream>
using namespace std;
int main() {
int a = 5;
int b = 5;
// 1. Prefix increment (++a): Tăng trước, dùng sau
cout << "Gia tri cua ++a: " << ++a << endl;
// Giải thích: a tăng lên 6 trước, sau đó lệnh cout mới in giá trị 6 ra.
// 2. Postfix increment (b++): Dùng trước, tăng sau
cout << "Gia tri cua b++: " << b++ << endl;
// Giải thích: Lệnh cout in giá trị hiện tại của b là 5 ra trước,
// sau khi in xong thì b mới âm thầm tăng lên 6.
// 3. Kiểm tra lại giá trị của b sau khi thực hiện b++
cout << "Gia tri cua b sau khi in dong tren: " << b << endl;
return 0;
}
2.4.4. Toán tử kết hợp
| Toán tử | Ví dụ | Tương đương | Ý nghĩa |
|---|---|---|---|
+= | x += 5 | x = x + 5 | Cộng và gán |
-= | x -= 5 | x = x - 5 | Trừ và gán |
*= | x *= 5 | x = x * 5 | Nhân và gán |
/= | x /= 5 | x = x / 5 | Chia và gán |
%= | x %= 5 | x = x % 5 | Chia lấy dư và gán |
<<= | x <<= 5 | x = x << 5 | Dịch trái và gán |
>>= | x >>= 5 | x = x >> 5 | Dịch phải và gán |
&= | x &= 5 | x = x & 5 | Bitwise AND và gán |
^= | x ^= 5 | x = x ^ 5 | Bitwise XOR và gán |
|= | x |= 5 | x = x | 5 | Bitwise OR và gán |
2.4.5. Toán tử bit
- Toán tử bit (bitwise operators) trong C++ là các toán tử thao tác trực tiếp trên các bit của toán hạng (nguyên).
- Gồm các toán tử:
&(and),|(or),^(xor),~(not hay lấy số bù 1),>>(shift bits right: dịch phải),<<(shift bits left: dịch trái)
Bảng chân trị các phép toán logic cơ bản:
| p | q | p & q (AND) | p ^ q (XOR) | p | q (OR) | ~p (NOT) |
|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 1 |
| 0 | 1 | 0 | 1 | 1 | 1 |
| 1 | 1 | 1 | 0 | 1 | 0 |
| 1 | 0 | 0 | 1 | 1 | 0 |
Ví dụ:
5 & 3Kết quả: 1 (0101 & 0011 = 0001)5 | 3Kết quả: 7 (0101 | 0011 = 0111)5 ^ 3Kết quả: 6 (0101 ^ 0011 = 0110)~5Kết quả: -6 (bit đảo ngược của 0101 là 1010, tương đương với -6)12 >> 3 = 1100 >> 3 = 0001 = 1(toán tử dịch phải)3 << 2 = 0011 << 2 = 1100 = 12(toán tử dịch trái)
2.4.6. Toán tử điều kiện - Conditional Ternary Operator
Toán tử điều kiện là một toán tử được áp dụng để xác định biểu thức thứ nhất hay thứ hai được thực hiện tùy theo giá trị đúng hay sai của một điều kiện. Cú pháp:
<điều kiện> ? biểu_thức_1> : <biểu_thức_2>
Ví dụ: Giữa hai số a,b hãy tìm số nào lớn hơn.
a > b ? max = a : min = b
2.4.7. Toán tử quan hệ - Relational operators
| Operator | Toán tử | Ký hiệu | Ví dụ | Giải thích |
|---|---|---|---|---|
| Greater than | Lớn hơn | > | x > y | Nếu x lớn hơn y → true (1)Ngược lại → false (0) |
| Less than | Nhỏ hơn | < | x < y | Nếu x nhỏ hơn y → true (1)Ngược lại → false (0) |
| Greater than or Equal to | Lớn hơn hoặc bằng | >= | x >= y | Nếu x lớn hơn hoặc bằng y → true (1)Ngược lại → false (0) |
| Less than or Equal to | Nhỏ hơn hoặc bằng | <= | x <= y | Nếu x nhỏ hơn hoặc bằng y → true (1)Ngược lại → false (0) |
| Equal to | Bằng | == | x == y | Nếu x bằng y → true (1)Ngược lại → false (0) |
| Not equal to | Khác | != | x != y | Nếu x khác y → true (1)Ngược lại → false (0) |
Ví dụ:
(7 == 5) // evaluates to false
(5 > 4) // evaluates to true
2.5.8. Toán tử luận lý - Logical operators
| Toán tử | Ký hiệu | Ví dụ | Giải thích |
|---|---|---|---|
| NOT | ! | !X | Toán tử một ngôi, thể hiện điều kiện "phủ định (not) X". |
| AND | && | X && Y | Toán tử hai ngôi, thể hiện điều kiện "X và (and) Y". |
| OR | || | X || Y | Toán tử hai ngôi, thể hiện điều kiện "X hoặc (or) Y". |
Ví dụ:
X Y !X X && Y X || Y True (nonzero) False (0) False (0) True (1) True (nonzero) True (nonzero) True (1) True (1) True (nonzero) False (0) False (0) True (1) False (0) True (nonzero) False (0) True (1) False (0) False (0) False (0) False (0)
Phân biệt giữa toán tử luận lý và toán tử bit
| Đặc điểm | Toán tử Luận lý (Logical) | Toán tử Bit (Bitwise) |
|---|---|---|
| Ký hiệu | &&, ||, ! | &, |, ^, ~, <<, >> |
| Đối tượng | Biểu thức Đúng/Sai (Boolean). | Các bit nhị phân của số nguyên. |
| Kết quả | Luôn là 0 (False) hoặc 1 (True). | Một giá trị số nguyên mới. |
| Cơ chế | Kiểm tra toàn bộ giá trị (0 hoặc khác 0). | Thao tác trên từng cặp bit tương ứng. |
Ví dụ:
#include <iostream>
using namespace std;
int main() {
int a = 2; // Nhị phân: 0010
int b = 1; // Nhị phân: 0001
// So sánh toán tử AND
cout << "Logical AND: " << (a && b) << endl; // Xuất ra 1
cout << "Bitwise AND: " << (a & b) << endl; // Xuất ra 0 (0000)
// So sánh toán tử OR
cout << "Logical OR: " << (a || b) << endl; // Xuất ra 1
cout << "Bitwise OR: " << (a | b) << endl; // Xuất ra 3 (0011)
return 0;
}
2.5. Biểu thức và độ ưu tiên toán tử
2.5.1. Biểu thức
Định nghĩa: Biểu thức được tạo thành được từ các toán hạng (Operand) (hằng số, biến số và hàm số) liên kết với nhau bằng các toán tử (Operator). Trong đó:
- Toán tử tác động lên các giá trị của toán hạng và cho giá trị có kiểu nhất định.
- Toán hạng: hằng, biến, lời gọi hàm...
Giá trị của biểu thức là giá trị kết quả từ các toán tử tác động lên các toán hạng tương ứng.
#include <iostream>
using namespace std;
int main() {
int a = 2 + 3; //Giá trị 2+3 là 5, nên a được khởi tạo là 5
int b = a / 5; //Giá trị a/5 là 1, nên b được khởi tạo là 1
int c = (a + b) * 5; //Biểu thức (a+b) được thực hiện trước, sau đó kết quả nhân với 5
return 0;
}
2.5.2. Độ ưu tiên toán tử
| Mức | Toán tử | Mô tả | Thứ tự thực hiện |
|---|---|---|---|
| 1 | :: | Phân giải phạm vi (Scope resolution) | |
| 2 | ++ --() []. -> | Hậu tố (Postfix increment/decrement) Gọi hàm, truy cập mảng Truy cập thành viên (biến/con trỏ) | Trái sang Phải |
| 3 | ++ --+ -! ~(type)* &sizeofnew delete | Tiền tố (Prefix increment/decrement) Cộng/Trừ nhất phân (Unary plus/minus) Logic NOT, Bitwise NOT Ép kiểu (C-style cast) Giải tham chiếu (Indirection), Lấy địa chỉ Kích thước kiểu dữ liệu Cấp phát/Giải phóng bộ nhớ | Phải sang Trái |
| 4 | .* ->* | Con trỏ tới thành viên (Pointer to member) | Trái sang Phải |
| 5 | * / % | Nhân, Chia, Chia lấy dư | Trái sang Phải |
| 6 | + - | Cộng, Trừ | Trái sang Phải |
| 7 | << >> | Dịch bit trái/phải | Trái sang Phải |
| 8 | < <= > >= | Các toán tử quan hệ (So sánh) | Trái sang Phải |
| 9 | == != | So sánh bằng và khác | Trái sang Phải |
| 10 | & | Bitwise AND | Trái sang Phải |
| 11 | ^ | Bitwise XOR | Trái sang Phải |
| 12 | | | Bitwise OR | Trái sang Phải |
| 13 | && | Logic AND | Trái sang Phải |
| 14 | || | Logic OR | Trái sang Phải |
| 15 | ?: | Toán tử điều kiện (Ternary) | Phải sang Trái |
| 16 | =+= -= *= /= %=<<= >>=&= ^= |= | Gán trực tiếp Gán kết hợp số học Gán dịch bit Gán toán tử bit | Phải sang Trái |
| 17 | throw | Ném ngoại lệ (Exceptions) | Phải sang Trái |
| 18 | , | Toán tử phẩy | Trái sang Phải |
Một biểu thức có nhiều phép toán, thì quy tắc thực hiện như sau:
- Thực hiện biểu thức trong
( )sâu nhất trước. - Thực hiện theo thứ tự ưu tiên các toán tử.
- Khi một biểu thức có hai toán tử có cùng mức độ ưu tiên: thực hiện phép toán từ trái sang phải hoặc từ phải sang trái sẽ theo quy ước chung của nhóm phép toán đó.
- Việc đặt tất cả các câu lệnh phụ trong dấu ngoặc đơn (ngay cả những câu lệnh không cần thiết vì mức độ ưu tiên của chúng) sẽ cải thiện khả năng đọc mã.
Ví dụ: Tính biểu thức:
int a = 5, b = 10, c = 15;
bool result = (a + b * c > b) && (c / a == b % c) || (a - b < c);Ta có:
(a + b * c > b) = ((a + (b * c)) > b) = ((5+(10*15)) > 10) = true(c / a == b % c) = ((c / a) == (b % c)) = (15 / 5 == 10 % 15) = false(a - b < c) = ((a – b) < c) = (5 - 10 < 15) = true
result = true && false || true(&&ưu tiên hơn||)
result = (true && false) || true
result = true
3. Nhập xuất dữ liệu
Trong lập trình, dữ liệu có thể gán bằng code, tính toán và lưu vào chương trình. Bên cạnh đó, ta có thể cho người dùng nhập dữ liệu và chương trình, chương trình thực hiện tính toán, biến đổi và trả về kết quả có thể tương tác với người dùng.
Trong môn Nhập môn lập trình, quá trình tương tác dữ liệu sẽ diễn ra ở hình thức đơn giản nhất là màn hình console. Ngoài ra, phần này sẽ giới thiệu cách đọc và ghi dũ liệu vào file.
3.1. Nhập xuất qua màn hình console
3.1.1. Thư viện nhập xuất
Có 2 loại thư viện nhập xuất trong C++
- Thư viện nhập xuất
<stdio.h>(kế thừa từ ngôn ngữ C) - Thư viện nhập xuất tiêu chuẩn
<iostream>của C++.
| Ngôn ngữ C++ | Ngôn ngữ C | |
|---|---|---|
| Khai báo thư viện và namespace | #include <iostream>using namespace std; | #include <stdio.h> |
| Lệnh nhập | cin >>cin.get, cin.getline,getline (<string>) | scanffgetc, getc, getchar, fgets |
| Lệnh xuất | cout << | printfputchar, putc, puts, fputs |
3.1.2. Câu lệnh xuất: cout
coutlà một đối tượng giúp bạn có thể hiện thị nội dung như : số nguyên, số thực, giá trị của biến, đoạn text ra màn hình. Để sử dụng đối tượng này bạn cần khai báo thư việniostreamvà sử dụngnamespace stdcoutđược sử dụng đi kèm với toán tử chèn<<- Cú pháp:
hoặc
#include <iostream>
...
std:: cout << Tham_số_1 << Tham_số_2 << ... << Tham_số_k;#include <iostream>
using namespace std;
...
cout << Tham_số_1 << Tham_số_2 << ... << Tham_số_k; - Tham số có thể:
- Chuỗi hằng
- Biến, hằng số, biểu thức, hàm
- Ký tự điều khiển (escape sequence)
Ký tự điều khiển (escape sequence): Gồm dấu
\và một ký tự
Ví dụ\ndùng để xuống dòng,\tcùng để cách vào một khoảng tab
- Ngoài
\n,endllà một đối tượng đặc biệt giúp bạn có thể xuống dòng khi sử dụngcout, bạn sẽ thêm đối tượng này trong câu lệnhcouttại vị trí bạn muốn xuống dòng.#include <iostream>
using namespace std;
int main() {
cout << "Hello UIT\n"; //Hiển thị dòng Hello UIT lên màn hình và xuống dòng
cout << 100 << endl; //Hiển thị số 100 lên màn hình và xuống dòng
cout << banKinh << endl; //Hiển thị giá trị của biến banKinh lên màn hình và xuống dòng
}Lưu ý: nếu không khai báo
namespace stdthì phải dùng đầy đủ cú phápstd::cout << std::endl
Ví dụ:
#include <iostream>
using namespace std;
int main(){
cout << "UIT" << endl;
cout << "TRUONG DAI HOC CNTT" << " " << "UIT" <<endl;
cout << 100 << " " << 200 << " " << 300 << endl;
int n=282828;
cout << "Gia tri cua bien n: " << n <<endl;
}
Output:
UIT
TRUONG DAI HOC CNTT UIT
100 200 300
Gia tri cua bien n : 282828
Lưu ý khi in giá trị của số thực float, double
Đối với kiểu dữ liệu số thực, đề bài thường yêu cầu bạn in ra các số này với độ chính xác nhất định, cụ thể là bao nhiêu chữ số phần thập phân
Mặc định chương trình sẽ in ra 6 chữ số có nghĩa kể cả phần nguyên và phần thập phân.
#include<iostream>
using namespace std;
int main() {
cout << 1.123456789 << " " << 1234.123456789 << " " << 123456.12345;
}Output:
1.12346 1234.12 123456
Để in ra giá trị số float, double với độ chính xác k chữ số sau dấu phẩy bạn thêm cụm << fixed << setprecision(k) trước tên biến.
Thư viện chứa đối tượng setprecision là thư viện <iomanip>, bạn cần thêm vào trước khi sử dụng.
#include <iostream>
#include <iomanip>
using namespace std;
int main() {
float a = 30.192387;
double b = 9.18293;
cout << fixed << setprecision(2) << a << endl;
cout << fixed << setprecision(10) << b << endl;
return 0;
}
Output
30.19
9.1829300000
Xác định độ rộng khi in với setw và setfill
Khi muốn in ra giá trị của 1 số, 1 biến hay đoạn text với độ rộng tương đương bao nhiêu dấu cách bạn có thể sử dụng setw(k) với k là độ rộng trước tên biến hay nội dung bạn muốn in.
Trong trường hợp bạn muốn thay thế các dấu cách khi nội dung bạn in ra không đủ độ rộng chỉ định bạn có thể sử dụng setfill(c) với c là ký tự bạn muốn thay thế cho các dấu cách trống.
#include <iostream>
#include <iomanip>
using namespace std;
int main(){
int n = 12345;
cout << setw(10) << n << endl;
cout << setw(10) << setfill('0') << n << endl;
cout << setw(10) << setfill('#') << "UIT" << endl;
return 0;
}
Output
12345
000012345
#######UIT
3.1.3. Câu lệnh nhập: cin
cinlà một đối tượng được sử dụng để nhập dữ liệu từ thiết bị nhập chuẩn (thông thường là bàn phím).- Tương tự như cout, để sử dụng
cinbạn cần khai báo thư viện<iostream>và sử dụngnamespace std cinđược sử dụng đi kèm với toán tử trích xuất (extraction operator)>>. Toán tử này sẽ điều hướng dữ liệu từ bàn phím vào biến mà bạn chỉ định.- Cú pháp:
#include <iostream>
// Cách 1: Sử dụng namespace std
using namespace std;
cin >> ten_bien;
// Cách 2: Không sử dụng namespace std
std::cin >> ten_bien; - Các biến
cincó thể nhập thuộc các kiểu dữ liệu cơ bảnint,long long,short,float,double,char,bool,string- Khi nhập số (
int,double,...)cinsẽ tự động bỏ qua các khoảng trắng, dấu tab hoặc dấu xuống dòng ở đầu cho đến khi thấy một con số. Nếu không tìm thấy số trong dữ liệu nhập, chương trình sẽ báo lỗi. - Khi nhập ký tự (
char)cin >>sẽ lấy ký tự không phải khoảng trắng đầu tiên mà nó thấy. - Khi nhập
bool, theo mặc định phải nhập một giá trị số tương ứng vớitrue(số khác 0) vàfalse(số 0)- Nếu không tìm thấy giá trị số trong dữ liệu nhập, chương trình sẽ báo lỗi.
- Trong nhiều trình biên dịch, chương trình sẽ gán cho biến
boolgiá trị mặc định là 0 và không báo lỗi. Đó là lý cho khi bạn nhậptrue(hayfalse) vào một biếnbool, giá trị biến sẽ là 0. Dotruevàfalselà một chuỗi không hợp lệ vớibool. - Nếu muốn chương trình hiểu true và false không phải một dữ liệu chuỗi, có thể dùng lệnh
cin >> boolalpha >> bien_boolđể nhập dữ liệu chobien_booldưới dạngtruehoặcfalse. Trường hợp này sẽ ngược lại với trên, kể cả khi bạn nhập giá trị số hợp lệ, giá trịboolvẫn sẽ là0.boolalphachỉ cho biếnboolnhận đúng giá trị khi nhậptruehoặcfalse.
- Khi nhập
string, giá trị nhận được sẽ là chuỗi các ký tự cho trước khi gặp khoảng trắng, tab hay xuống dòng. Ta sẽ xem kỹ hơn hướng xử lý khi nhập chuỗi ở bên dưới.
- Khi nhập số (
- Có thể nhập liên tiếp cho nhiều biến trong cùng một câu lệnh, với kiểu dữ liệu không nhất thiết giống nhau
Khi đó, các biến sẽ được phân cách bằng một ký tự trắng (white space characters). White space character bao gồm:
cin >> bien_1 >> bien_2 >> ... >> bien_k;- Ký tự khoảng trắng (ASCII code = 32): Khi nhấn SPACE
- Ký tự tab
\t: Khi nhấn TAB - Ký tự xuống dòng
\n: Khi nhấn ENTER
Nhập chuỗi (string)
Vì lý do trên, Khi sử dụng cin để nhập một chuỗi ký tự, chương trình sẽ ngừng đọc ngay khi gặp khoảng trắng đầu tiên.
Nếu muốn nhập một chuỗi có dấu cách (ví dụ: Họ và tên), ta cần dùng hàm getline()
#include <iostream>
#include <string> // Cần thêm thư viện này để dùng string và getline
using namespace std;
int main() {
string hoTen, hoTenFull;
cout << "Nhap ho va ten 1: ";
cin >> hoTen;
cout << "Nhap ho va ten 2: ";
getline(cin, hoTenFull); // Doc toan bo dong text
cout << endl << hoTen << endl << hoTenFull;
return 0;
}
Input:
Chao Ban hoc tapOutput:
Nhap ho va ten 1: Chao Ban hoc tap
Nhap ho va ten 2:
Chao
Ban hoc tap
Trong ví dụ trên, khi nhập xong Chao Ban hoc tap và nhấn Enter, toàn bộ chuỗi này (bao gồm cả các khoảng trắng và ký tự xuống dòng \n) sẽ được đưa vào bộ nhớ đệm.
- Khi lệnh
cin >>chỉ tìm và lấy các ký tự không phải khoảng trắng. Nó đọc chữ "Chao" và dừng lại ngay khi chạm mặt khoảng trắng đầu tiên. Quan trọng là, nó để lại khoảng trắng sau đó cùng phần nội dung phía sau trong bộ nhớ đệm. - Lệnh
getline(cin, hoTenFull);bắt đầu đọc ngay tại vị trí hiện tại của bộ nhớ đệm (vị trí dấu cách màcinvừa bỏ lại) cho đến khi gặp ký tự xuống dòng\n. Vì vậy, nó thu nhận cả dấu cách ở đầu, khiến biếnhoTenFullcó giá trị là " Ban hoc tap" (bao gồm khoảng trắng).
Hiện tượng xuất hiện kí tự thừa này cũng xảy ra tương tự với \n, \t khi thực hiện getline(cin, ...) sau cin >>. Lệnh cin >> không đọc những ký tự này mà đẩy nó sang phần dữ liệu tiếp theo của getline, nên dữ liệu in ra không như mong muốn.
Để loại bỏ ký tự rác này, C++ cung cấp hàm cin.ignore() trước khi getline để dọn dẹp bộ nhớ đệm trước khi thực hiện lệnh nhập chuỗi tiếp theo.
#include <iostream>
#include <string>
using namespace std;
int main() {
string hoTen, hoTenFull;
cout << "Nhap ho va ten 1: ";
cin >> hoTen;
cin.ignore();
cout << "Nhap ho va ten 2: ";
getline(cin, hoTenFull);
cout << endl << hoTen << endl << hoTenFull;
return 0;
}
Input:
Chao Ban hoc tapOutput:
Nhap ho va ten 1: Chao Ban hoc tap
Nhap ho va ten 2:
Chao
Ban hoc tap
Ban hoc tapin ra không chứa ký tự thừa, kể cả\nhay\t.
3.2. Nhập xuất vào file
Ngoài tương tác với dữ liệu qua màn hình console, C++ còn cung cấp các thư viện nhận và xuất dữ liệu vào các file trong hệ thống, giúp đơn giản quá trình nhập xuất dữ liệu thủ công.
Khi sử dụng file ta cần phải khai báo thư viện fstream với cú pháp: #include <fstream>
Khi xử lí file trong C++ ta có 3 class là:
ifstreamlà class để đọc dữ liệu đầu vào từ file
Ví dụ khai báo một biến kiểuifstreamđể đọc dữ liệu từ một file có tên là input.txt:ifstream ip("input.txt");ofstreamlà class để ghi dữ liệu vào (o viết tắt của out, f viết tắt của file) Ví dụ khai báo một biếnofstreamđể mở file:ofstream op;fstreamlà class để đọc hoặc ghi dữ liệu. Ta có thể thay thế 2 từ khóa trên bằng từ khóafstream
Đọc ghi file thường có các chế độ (mode) định dạng đi kèm như sau:
ios::indùng để Mở cho các hoạt động đầu vào. (mode mặc định củaifstream)ios::outdùng để mở cho các hoạt động đầu ra. (mode mặc định củaofstream)ios::binarydùng để mở file nhị phân.ios::atelà đặt vị trí con trỏ ở cuối file khi chúng ta mở file.ios::applà khi file đã có sẵn data thì dữ liệu sẽ được thêm vào cuối file.ios::trunclà khi ta chèn dữ liệu vào file, thì csc dữ liệu cũ sẽ bị xóa hết.
Để ghi dữ liệu vào file ta dùng cú pháp: nameFile<<"Data";
Để đọc dữ liệu từ file ta dùng cú pháp: nameFile>>value;
Để mở file bất kì ta dùng hàm open("nameFile", mode);
#include <bits/stdc++.h>
#include <fstream>
using namespace std;
int main() {
ifstream input("C:\\Users\\minhh\\OneDrive\\Desktop\\input.txt");
fstream output;
output.open ("C:\\Users\\minhh\\OneDrive\\Desktop\\input.txt", ios::out);
string str;
input >> str; // lấy giá trị biến str từ file input
cout << str; // in ra console
output >> "Hello World"; // in Hello World vào file input
input.close(); // đóng file input
output.close(); // đóng file output
return 0;
}
Khi sử dụng file trong C++ ta cần lưu ý liên kết tới file ta cần sử dụng ở đây bình thường ta copy link sẽ là C:\Users\minhh\OneDrive\Desktop\input.txt sẽ bị lỗi do \ là một kí tự đặc biệt và nó sẽ hiểu \U \m \O \D \i là một lệnh giống như \n (trỏ xuống dòng) vì vậy để ta coi \ là một char bình thường ta cần phải thêm \ trước \ tức là \\ như code mẫu trên.
Bình thường khi chúng ta lấy dữ liệu từ bàn phím ta sẽ sử dụng câu lệnh cin >> và in dữ liệu ra màn hình console ta dùng cout <<, và tương tự với đọc dữ liệu từ file và ghi dữ liệu vào file ta chỉ cần thay cin thành tên file cần đọc dữ liệu, và thay cout thành tên file cần ghi dữ liệu vào.
Nếu chỉ dùng input >> str; thì ta chỉ đọc được chuỗi cho tới khi nó gặp dấu cách. Muốn lấy dữ liệu lấy được cả dấu cách ta dùng hàm getline(input,str);