---
title
Những kiến thức cơ bản (Phần 2)
---
Những kiến thức cơ bản (Phần 2)
===
---
###### ✍️ Author: 2School Guideline
###### 📋 Content:
[TOC]
---
# Input và output
## 1. Input
* Nhập dữ liệu mà đề bài cho để giải quyết bài toán là một phần không thể thiếu trong một chương trình trong lập trình thi đấu. Để nhập dữ liệu từ bàn phím ta có thể dùng câu lệnh ```cin``` để nhập vào một hoặc nhiều giá trị từ bàn phím vào biến với cú pháp như sau:
```cpp=
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, m; // Khai báo hai biến n và m với kiểu dữ liệu int (integer)
cin >> n; // Nhập từ bàn phím giá trị của n
cin >> n >> m; // Nhập từ bàn phím giá trị của cả n và m
return 0;
}
```
**Ghi chú:**
- Các biến dùng để lưu giá trị cần phải được khai báo trước.
- Nếu muốn nhập dữ liệu nhiều phần tử hay nhập dữ liệu từ mảng (ta sẽ được học ở dưới), ta có thể sử dụng vòng lặp `for` kết hợp với câu lệnh `cin`.
## 2. Output
* Bên cạnh việc nhập dữ liệu thì chúng ta cũng cần phải xuất kết quả bài toán để có thể nắm được kết quả của bài toán. Ta có thể xuất dữ liệu mà mình mong muốn ra màn hình bằng câu lệnh ```cout``` với cú pháp như sau:
```cpp=
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, m; // Khai báo hai biến n và m với kiểu dữ liệu int (integer)
cin >> n >> m; // Nhập từ bàn phím giá trị của cả n và m
cout << n << " " << m; // Xuất dữ liệu (giá trị hai biến n và m) ra màn hình
return 0;
}
```
## 3. Nhập xuất file
* Trong các cuộc thi offline như Học sinh giỏi thành phố, Tuyển sinh 10 và nhiều cuộc thi lớn khác, các dữ liệu thường không được nhập, xuất từ bàn phím mà sẽ được nhập, xuất từ file. Vì thế sử dụng file để nhập, xuất là kiến thức mà chúng ta cần phải nắm vững.
* Đầu tiên, chúng ta cần phải tạo file **input** và **output** đúng với tên mà đề bài cho.

Ví dụ: Trong bài này, file input đầu vào là `BAI1.INP`, file output đầu ra kết quả là `BAI1.OUT`.
* Bước tiếp theo, chúng ta cần khai báo câu lệnh để chương trình nhận biết các file nhập, xuất dữ liệu đầu vào và kết quả:
File đầu vào:
```cpp
freopen("FILENAME.INP", "r", stdin);
```
File đầu ra:
```cpp
freopen("FILENAME.OUT", "w", stdout);
```
**Ghi chú:** `FILENAME` là tên file mà đề bài cho.
* Ta sẽ dùng câu lệnh ```cin``` và ```cout``` mà ta đã học ở trên để có thể nhập và xuất dữ liệu ra file.
## 4. Câu lệnh tăng tốc độ nhập, xuất
* Trong C++, để tăng tốc độ nhập, xuất của chương trình ta có thể dùng câu lệnh sau:
```cpp
ios_base::sync_with_stdio(0);
cin.tie(NULL);
cout.tie(NULL);
```
* Với các bài toán mà bạn thấy rằng thuật toán của mình có thời gian chạy có thể vượt qua một tí thời gian giới hạn thì câu lệnh trên sẽ giúp ích cho bạn rất nhiều. Mặc dù thế, câu lệnh trên không thể giúp giảm quá nhiều thời gian mà chương trình chạy, thời gian chương trình thực thi vẫn hầu hết tùy thuộc vào thuật toán mà bạn thực hiện.
* Cách hoạt động của câu lệnh trên khá phức tạp nên các bạn chỉ cần sử dụng chứ không cần thiết biết chúng hoạt động ra sao, còn nếu muốn hiểu rõ câu lệnh trên hoạt động như nào, bạn có thể tham khảo: https://www.geeksforgeeks.org/fast-io-for-competitive-programming/
# Mảng
## 1. Giới thiệu
* Mảng là cấu trúc dữ liệu cơ bản được sử dụng vô cùng phổ biến trong **C++**. Mảng là một dãy các phần tử có cùng một kiểu dữ liệu. Vậy tại sao chúng ta cần phải dùng mảng thay vì khai báo nhiều biến liên tục?
* Vì nếu bài toán cho ta dữ liệu quá nhiều giá trị như: Tính tổng số lương của một công ty có 1000 nhân viên. Việc khai báo từng biến rồi tính toán là được vô cùng mất thời gian, trong khi đó đối với mảng chỉ cần một lần khai báo thì đã làm được công việc đó.
## 2. Mảng tĩnh
* Mảng tĩnh là loại mảng thông thường được sử dụng trong C++ vì tính tiện lợi trong khai báo và sử dụng của chúng. Kích thước của mảng này được xác định khi khai báo và không bao giờ thay đổi nên được gọi là mảng tĩnh. Mảng tĩnh được khai báo với cú pháp như sau:
```
<kiểu dữ liệu> <tên biến mảng>[<số phần tử>];
```
Ví dụ:
```cpp
int a[1000];
```
*Ở ví dụ trên, ta khai bảng mảng a gồm 1000 phần tử có cùng kiểu dữ liệu int.*
**Ghi chú:**
- Nếu khai báo mảng ngoài chương trình chính thì tất cả các phần tử của mảng sẽ được gán bằng 0 và cũng có thể khai báo với nhiều phần tử hơn trong chương trình chính.
- Số lượng phần tử của mảng có giới hạn (khoảng $10^8$ ) nên chúng ta cần chú ý để tránh bị lỗi.
* Để truy xuất giá trị của phần tử trong mảng, ta chỉ cần dùng cú pháp sau:
```
<tên biến mảng>[<chỉ số thứ i>];
```
Ví dụ:
```cpp
int a[5];
a[0] = 1; // Hợp lệ
a[3] = 2; // Hợp lệ
a[5] = 0; // Không hợp lệ
```
**Ghi chú:**
- Phần tử của mảng trong cpp bắt đầu từ 0 và chúng ta chỉ có thể truy xuất phần tử có vị trí lớn nhất là `số phần tử của mảng khi khai báo - 1`.
## 3. Mảng động (Vector)
* Vector là một cấu trúc dữ liệu có sẵn trong thư viện STL, được sử dụng rất nhiều để thay thế mảng thông thường. Không như mảng tĩnh khi phải có một số lượng phần tử nhất định được khai báo trước, dễ gây lãng phí bộ nhớ khi không sử dụng, vector có thể có số lượng phần tử động, không cần khai báo trước nên sẽ không gây ra dư thừa, lãng phí bộ nhớ.
* Vector được khai báo với cú pháp: `vector<kiểu dữ liệu> tên biến`.
* Còn phần tử sẽ được đẩy vào cuối vector bằng câu lệnh: `tên biến.push_back(giá trị)`.
Ví dụ:
```cpp=
#include <bits/stdc++.h>
using namespace std;
int main() {
vector<int> hsg; // Khai báo vector
hsg.push_back(5); // Đẩy phần tử vào vector
hsg.push_back(3);
hsg.push_back(2);
for (int i = 0; i < 3; ++i)
cout << hsg[i] << " "; // Khi này vector sẽ xuất ra: 5 3 2
return 0;
}
```
**Ghi chú:**
- Cách truy cập vào phần tử của vector cũng giống như cách truy cập vào phần tử của mảng thường.
- Số lượng phần tử của vector cũng sẽ có giới hạn giống như mảng tĩnh thông thường (khoảng $10^8$), chớ nhầm lẫn làm chương trình bị sai.
## 4. Mảng nhiều chiều
* Mảng nhiều chiều có thể được nói một cách dễ hiểu là mảng của mảng một chiều với dữ liệu được lưu trữ dưới dạng bảng (hàng và cột). Chúng ta sẽ cùng tìm hiểu cách sử dụng mảng hai chiều và ba chiều, từ đó cũng có thể rút ra những mảng có nhiều chiều hơn.
### 4.1 Mảng 2 chiều
Ví dụ:
```cpp
int a[3][4];
```
* Mảng a trên là một mảng 2 chiều với kích thước là 3 x 4 = 12. Chúng ta có thể hình dung mảng 2 chiều có 3 cột 4 hàng này như hình dưới:

* Để truy xuất giá trị của phần tử trong mảng, ta sẽ sử dụng cú pháp:
``` <tên biến mảng>[<chỉ số dòng thứ x>][<chỉ số cột thứ y>]; ```
**Lưu ý:**
- Mảng 2 chiều cũng sẽ bắt đầu từ chỉ số 0 nên chúng ta phải luôn đảm bảo các chỉ số của mảng đều trong phạm vi của mảng.
* Từ cách truy xuất phần tử, chúng ta sẽ đi qua một ví dụ về cách nhập, xuất mảng 2 chiều:
```cpp=
#include <bits/stdc++.h>
using namespace std;
int main() {
int a[10][10]; // Khai báo mảng 2 chiều
// Nhập giá trị cho các phần tử từ [0][0] đến [2][2]
for (int i = 0; i < 3; ++i)
for (int j = 0; j < 3; ++j)
a[i][j] = j; // Cho giá trị của mỗi ô là số cột của ô đó
// Xuất giá trị của mảng
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j)
cout << a[i][j] << " ";
cout << "\n";
}
// Giá trị xuất ra sẽ là:
// 0 1 2
// 0 1 2
// 0 1 2
return 0;
}
```
Ở đây, ta đã sử dụng 2 vòng lặp for lồng nhau để nhập, xuất các phần tử mảng:
- Vòng lặp bên ngoài từ `i = 0` đến `i = 2` truy cập vào các hàng của mảng.
- Vòng lặp bên trong từ `j = 0` đến `j = 2` truy cập các cột của mảng.
### 4.2 Mảng 3 chiều
* Mảng 3 chiều cũng sẽ hoạt động như mảng 2 chiều. Ví dụ:
```cpp
int a[3][4][2];
```
* Trong mảng a này, số phần tử sẽ là 3 x 4 x 2 = 24.
* Cách mảng 3 chiều truy xuất cũng giống như mảng 2 chiều, nhưng có thêm chiều thứ 3:
``` <tên biến mảng>[<chỉ số 1>][<chỉ số 2>][<chỉ số 3>];```
* Với cách truy xuất này, chúng ta sẽ xem một chương trình ví dụ về cách nhập, xuất mảng 3 chiều:
```cpp=
#include <bits/stdc++.h>
using namespace std;
int main() {
int a[10][10][10]; // Khai báo mảng 2 chiều
int d = 0;
// Nhập giá trị d tăng dần cho các phần tử từ [0][0][0] đến [1][1][1]
for (int i = 0; i < 2; ++i)
for (int j = 0; j < 2; ++j)
for (int z = 0; z < 2; ++z) {
a[i][j][z] = d; // Cho giá trị của mỗi ô là d tăng dần
d++;
}
// Xuất giá trị của mảng
for (int i = 0; i < 2; ++i)
for (int j = 0; j < 2; ++j)
for (int z = 0; z < 2; ++z) {
cout << "a[" << i << "][" << j << "][" << z << "] = " << a[i][j][z];
cout << "\n";
}
// Giá trị xuất ra của mảng là:
// a[0][0][0] = 0
// a[0][0][1] = 1
// a[0][1][0] = 2
// a[0][1][1] = 3
// a[1][0][0] = 4
// a[1][0][1] = 5
// a[1][1][0] = 6
// a[1][1][1] = 7
return 0;
}
```
Cách mảng 3 chiều nhập, xuất tương tự như mảng 2 chiều, nhưng vì có 3 chiều nên ta phải dùng 3 vòng lặp for để nhập, xuất.
### 4.3. Kết luận
* Ta thấy rằng các mảng đa chiều sẽ có cách hoạt động giống nhau, là bao gồm của các mảng chiều nhỏ hơn.
* Số lượng phần tử của mảng đa chiều sẽ là độ lớn các kích thước của các chiều nhân lại với nhau.
# Struct (cấu trúc)
**Chú ý: Tất cả các code ví dụ từ đây trở xuống đều là mã giả**.
## 1. Khái niệm
* Xét trường hợp chúng ta cần lưu trữ thông tin của nhiều cuốn sách trong thư viện, mỗi cuốn sách có nhiều loại thông tin như: Tiêu đề, tác giả, thể loại và ID. Trong các kiểu dữ liệu nguyên thủy ta đã học, không có kiểu nào có thể lưu trữ cùng lúc nhiều thông tin như thế. Khi đó, lập trình viên sẽ phải tìm cách tạo ra các kiểu dữ liệu mới để lưu trữ tất cả thông tin của một cuốn sách (là kiểu dữ liệu tự định nghĩa).
* Struct và class là 2 cách để gom nhiều biến khác nhau thành 1 kiểu dữ liệu mới tự định nghĩa. Không giống như mảng, struct/class có thể chứa nhiều kiểu dữ liệu khác nhau (int, bool char, string,...). Nhưng trong bài viết này, chúng ta sẽ chỉ tìm hiểu về `struct` vì nó đơn giản hơn và đã đủ để sử dụng trong lập trình thi đấu.
## 2. Khai báo
* Để khai báo 1 struct mới, dùng từ khóa `struct` kèm theo tên của struct và khai báo từng thành viên bên trong dấu ngoặc nhọn. Ta có thể coi đây như 1 kiểu dữ liệu. Điều này có nghĩa là ta có thể tạo biến với kiểu dữ liệu này ở bất cứ đâu trong chương trình.
* Ví dụ:
```cpp
struct high_school_guideline // khai báo struct
{
int num; // Thành viên 1 (kiểu int)
string s; // Thành viên 2 (kiểu string)
};
```
## 3. Truy cập
* Để truy cập thành viên trong struct, dùng cú pháp dấu `.`
* Ví dụ:
```cpp
struct high_school_guideline // khai báo struct
{
int num; // Thành viên 1 (kiểu int)
string s; // Thành viên 2 (kiểu string)
};
// gán giá trị cho các thành viên
high_school_guideline.num = 123456789;
high_school_guideline.s = "Hello World";
// in ra các thành viên của struct
cout << high_school_guideline.num << "\n"; //123456789
cout << high_school_guideline.s << "\n"; // Hello World
```
# Char (kí tự) và String (chuỗi kí tự)
## 1. Char (kí tự)
* Trong C++, `char` là một kiểu dữ liệu đặc biệt, nó vừa là kiểu số nguyên, cũng vừa là kiểu ký tự. Do đó, kiểu `char` tuân thủ tất cả các quy tắc của một số nguyên bình thường (+-*/…)
* Một biến có kiểu dữ liệu `char` sẽ có độ lớn 1 bytes (8 bits), dùng để lưu trữ một ký tự trong bảng mã ASCII như (a, b, c, … 1, 2, 3, …)
*Ví dụ:*
```cpp
char ch1 = 'K'; // khởi tạo biến char có tên ch1 với ký tự 'K' (mã ASCII 75)
char ch2 = 75; // khởi tạo biến char có tên ch2 với mã ASCII 75 (ký tự 'K')
char ch3; // khai báo biến kiểu char có tên ch3
ch1 = 75; // gán mã ASCII 75 (ký tự 'K') cho biến char có tên ch1
```
**Lưu ý 1:** Vì bản chất của kiểu ```char``` cũng là số nguyên, nên khi khởi tạo hoặc gán giá trị cho biến kiểu `char`, bạn hoàn toàn có thể dùng số nguyên (mã ASCII) hoặc ký tự.
```cpp
char ch1 = 75; // mã ASCII 75 (ký tự 'K')
char ch2 = 'K'; // ký tự 'K' (mã ASCII 75)
```
**Lưu ý 2:** Ký tự số nguyên không giống với mã ASCII số nguyên, ta cần phân biệt điều này để tránh nhầm lẫn khi khởi tạo một biến character
```cpp
char ch1 = '7'; // khởi tạo biến character với ký tự '7'
char ch2 = 7; // khởi tạo biến character với mã ASCII 7
```
## 2. String (chuỗi kí tự)
* `string` là 1 kiểu dữ liệu dùng để lưu trữ văn bản.
* 1 biến có kiểu `string` chứa 1 chuỗi kí tự được ghi bên trong cặp dấu ngoặc kép `" "`.
* Ví dụ:
```cpp
string s = "Hello World";
```
* Để sử dụng kiểu string, mình cần phải gọi thư viện `<string>`.
* Ví dụ:
```cpp
#include <string>
string s = "Hello World";
```
* Nhưng đây là 1 thư viện đã có sẵn khi gọi thư viện `<bits/stdc++.h>` nên mình không cần gọi thêm thư viện `<string>` nữa.
## 3. Nối chuỗi
* Toán tử ```+``` có thể được dùng để thêm 1 string này vào sau 1 string khác. Điều này gọi là nối chuỗi.
* Ví dụ:
```cpp
string s = "I love ";
string t = "2SG";
string high_school_guideline = s + t;
cout << high_school_guideline; // I love 2SG
```
**Lưu ý:**
* Việc kết hợp chuỗi và cộng 2 số nguyên có kết quả khác nhau.
* Ví dụ:
```cpp
int a = 20, b = 10;
int sum1 = a + b;
cout << sum1 << "\n"; // 30
string s = "20", t = "10";
string sum2 = s + t;
cout << sum2 << "\n"; // 2010
```
## 4. Các hàm cơ bản
* `getline`: hàm để nhập 1 string vào, đặc biệt khi string có dấu cách (ví dụ: "abc d")
* Ví dụ:
```cpp
Input: "high school guideline"
string s;
// dùng cin
cin >> s;
cout << s; // high
// dùng getline
getline(cin, s);
cout << s; // high school guideline
```
**Lưu ý:** Khi muốn dùng ```getline```, cần lưu ý nếu trước đó mình có nhập ít nhất 1 số thuộc kiểu ```int``` hoặc ```double``` hay không. Nếu có, thì ta cần ghi thêm dòng ```cin.ignore()``` trước khi dùng ```getline```. Vì khi nhập số vào, sẽ luôn tồn tại 1 dấu xuống dòng ngay sau đó, nếu dùng ```getline``` ngay lập tức, dữ liệu ta nhận vào sẽ là 1 xâu rỗng.
Ví dụ:
**Input:**
```
1 2 3
2sg
```
- Mã giả:
```cpp
// không có cin.ignore()
int a, b, c;
cin >> a >> b >> c;
string s;
getline(cin, s);
cout << s; // s = "" (xâu rỗng)
// có cin.ignore()
int a, b, c;
cin >> a >> b >> c;
cin.ignore();
string s;
getline(cin, s);
cout << s; // s = "2sg"
```
* `endl` và `"\n"`:
* `endl` thêm dòng mới và đẩy bộ đệm của cout vào thiết bị đầu ra.
* `"\n"` chỉ thêm dòng mới mà không thực hiện đẩy bộ đệm.
* Về hiệu suất `"\n"` sẽ chạy nhanh hơn `endl` thế nên trong lập trình thi đấu nên sử dụng `"\n"` hơn là `endl`.
## 5. Bảng mã ASCII
### 5.1. Khái quát
* ASCII là viết tắt của cụm từ **American Standard Code for Information Interchange**, có nghĩa là **Chuẩn mã trao đổi thông tin Hoa Kỳ**. Đây là bộ mã hóa ký tự cho bảng chữ cái La Tinh và được dùng để hiển thị văn bản trong máy tính.
* Về cơ bản, bạn có thể hiểu ASCII là một bộ mã giúp máy tính có hiểu và hiển thị được các ký tự mà bạn muốn nhập vào máy tính hay đơn giản hơn là các ký tự trên bàn phím máy tính chuẩn Anh. Tập hợp các mã ASCII tạo thành bảng mã ASCII.
* Có 2 loại ký tự trong bảng mã ASCII:
* Ký tự không in được (ký tự điều kiển) có mã từ 0 -> 31 và 127 bao gồm các ký tự dùng để điều khiển hay dùng như: backspace (7), new line (10), escape (27), bell (7) …Có 2 loại ký tự trong bảng mã ASCII:
* Ký tự in được có mã từ 32 -> 126 bao gồm các chữ cái (a, b, c, …), số (1, 2, 3, …), toán tử (+, -, …), dấu câu (~, {, ^ …)
### 5.2. Bảng mã ASCII chuẩn
* Bảng mã ASCII chuẩn còn được gọi là bảng mã ASCII cơ bản quy định bộ mã hóa cho những ký tự đơn giản nhất: 128 ký tự bao gồm các ký tự điều khiển, bảng chữ cái, các dấu,…

# Phạm vi của biến
## 1. Biến toàn cục
- Một biến được định nghĩa ở bên ngoài của tất cả các hàm thì chúng được gọi là biến toàn cục.
- Phạm vi của biến toàn cục là trong toàn bộ chương trình. Tức là, biến toàn cục có thể được sử dụng và bị thay đổi giá trị trong bất cứ đâu trong chương trình sau khi được khai báo. Biến toàn cục chỉ bị hủy khi chương trình kết thúc.
- Biến toàn cục được khởi tạo bằng giá trị mặc định:
* `0`: cho `int`, `bool`, `float`
* `""`: cho `string`
* `''`: cho `char`
- Ví dụ:
```cpp=
#include <bits/stdc++.h>
using namespace std;
int a, b;
void print()
{
cout << a << " " << b;
}
int main()
{
cout << a << " " << b << "\n"; // 0 0
print();
// kết quả trả về là:
// 0 0
// 0 0
return 0;
}
```
## 2. Biến cục bộ
- Một biến được khai báo trong hàm (bên trong thân hàm giữa cặp dấu ngoặc nhọn ```{ }``` ) được gọi là biến cục bộ.
- Phạm vi của biến cục bộ chỉ giới hạn trong hàm mà biến được định nghĩa. Tức là biến cục bộ chỉ tồn tại và chỉ có thể được truy cập bên trong hàm. Biến cục bộ sẽ bị hủy khi hàm kết thúc.
* Ta sẽ xét đoạn code sau:
```cpp=
#include <bits/stdc++.h>
using namespace std;
void test()
{
int var1; // biến cục bộ trong hàm test
var1 = 1;
cout << var; // lỗi: biến "var" chưa được khai báo
}
int main()
{
int var = 1; // biến cục bộ của hàm main
test();
var1 = 1; // lỗi: biến var1 chưa được khai báo
return 0;
}
```
- Đoạn code trên sẽ báo **lỗi biên dịch** do biến `var` chỉ mới được khai báo ở hàm `main`, còn ở hàm `test` ta chỉ mới khai báo biến `var1`. Vì thế khi sử dụng biến `var` ở hàm `test` sẽ báo lỗi chưa khai báo biến `var` do ta chỉ mới khai báo biến `var` ở hàm `main`, không thể sử dụng được trong hàm `test`. Tương tự thế với việc ta sử dụng biến `var1` ở hàm `main` cũng sẽ báo lỗi tương tự.