You are on page 1of 24

Kiểu cấu trúc : [Cpp Advanced]

Bài tập
Bạn hãy viết chương trình tạo ra struct Student với hai thuộc tính là name và age. Sau đó
hãy khai báo biến với kiểu dữ liệu Student và nhập dữ liệu cho 2 thuộc tính của biến này và
hiển thị ra màn hình:

Name: {P1}
Age: {P2}

Với {P1} là tên vừa nhập, {P2} là tuổi vừa nhập


Ví dụ nếu bạn nhập:

Viet
32

Thì màn hình sẽ hiển thị ra:

Name: Viet
Age: 32

Lý thuyết
Như bạn đã biết trong C++ có các kiểu dữ liệu có sẵn như string, int, short, long long,
... bài này bạn sẽ học được cách tạo ra một kiểu dữ liệu với struct.

Kiểu struct cho phép bạn kết hợp các kiểu dữ liệu lại thành 1 kiểu dữ liệu mới do bạn tự
định nghĩa. Ví dụ nếu bạn cần viết chương trình lưu thông tin về tên và tuổi của 100 sinh
viên, với cách làm thông thường bạn sẽ tạo ra 2 mảng giống như sau:

string names[100];
int ages[100];

Với struct thì bạn sẽ định nghĩa kiểu dữ liệu Student với 2 thuộc tính là name và age giống
như sau:

// Khai báo struct student với 2 thuộc tính là name và age


struct Student {
string name;
int age;
};

và bây giờ thay vì dùng 2 mảng names và ages thì bạn dùng mảng các đối tượng Student:

Student students[100];

Có thể thấy viết Student students[100]; sẽ rõ nghĩa hơn là viết 2 mảng names và ages. Hơn
nữa với cách làm cũ nếu chương trình của bạn cần lưu trữ 10 thuộc tính của học sinh thì
bạn phải tạo ra 10 mảng và nếu có truyền thông tin học sinh vào hàm thì hàm phải có 10
tham số đầu vào. Còn với struct thì bạn chỉ cần truyền vào hàm 1 tham số.
Để truy xuất vào các thuộc tính của một đối tượng kiểu struct bạn có thể
gọi tên_đối_tượng.tên_thuộc_tính. Ví dụ:

#include<iostream>

using namespace std;

// Khai báo struct student với 2 thuộc tính là name và age


struct Student {
string name;
int age;
};

int main() {
// Tạo ra 1 đối tượng kiểu Student để lưu thông tin của 1 sinh
viên
Student s1;
s1.name = "Blackcat";
s1.age = 24;
cout << "Name: " << s1.name << endl;
cout << "Age :" << s1.age;
return 0;
}

Kết quả khi chạy chương trình:

Name: Blackcat
Age :24

Đọc tới đây bạn đã có thể làm được bài này, hãy quay lại phần bài tập và làm thử.
Nếu bạn chưa biết cách làm bài này với struct thì có thể xem phần hướng dẫn bên dưới:

Hướng dẫn
Code mẫu:

#include<iostream>

using namespace std;

struct Student {
string name;
int age;
};

int main() {
Student s1;
cin >> s1.name;
cin >> s1.age;
cout << "Name: " << s1.name << endl;
cout << "Age: " << s1.age;
return 0;
}

Bài tập
Viết chương trình khai báo kiểu dữ liệu SinhVien với các thuộc tính string name, int age,
int score. Sau đó bạn hãy nhập từ bàn phím mảng n phần tử các SinhVien (mảng này
được cấp phát động) và hiển thị ra màn hình thông tin của sinh viên có điểm cao nhất giống
như sau:

Name : {P1}
Age: {P2}
Score: {P3}

Với {P1} là tên của sinh viên có điểm cao nhất.


{P2} là tuổi của sinh viên có điểm cao nhất.

{P3} là điểm của sinh viên có điểm cao nhất.

Đầu vào luôn đảm bảo chỉ có 1 sinh viên có điểm cao nhất.
Ví dụ nếu bạn nhập

2
Binh 19 7
An 20 8

Thì màn hình sẽ hiển thị ra:

Name: An
Age: 20
Score: 8

Giải thích: An là sinh viên có điểm cao hơn Bình nên màn hình sẽ hiển thị ra thông tin của
An.

Hướng dẫn
Code mẫu:

#include<iostream>

using namespace std;

struct Student {
string name;
int age;
int score;
};

int main() {
int n;
cin >> n;
Student *students = new Student[100];
for (int i = 0; i < n; i++) {
cin >> students[i].name >> students[i].age >>
students[i].score;
}

Student s = students[0];
for (int i = 1; i < n; i++) {
if (students[i].score > s.score) {
s = students[i];
}
}

cout << "Name: " << s.name << endl;


cout << "Age: " << s.age << endl;
cout << "Score: " << s.score;

delete[] students;
return 0;
}

Bài tập
Viết chương trình khai báo kiểu dữ liệu Student (sinh viên) với các thuộc tính string name,
int age và phương thức display(). Sau đó bạn hãy nhập từ bàn phím mảng n phần tử
các sinh viên và dùng phương thức display() để hiển thị ra màn hình thông tin các sinh
viên.
Ví dụ nếu bạn nhập

2
Binh 19
An 20

Thì màn hình sẽ hiển thị ra:

Name: Binh
Age: 19
Name: An
Age: 20

Lý thuyết
Ngoài các thuộc tính ra thì bạn còn có thể định nghĩa các phương thức bên trong
một Struct. Ví dụ về Struct khai báo và sử dụng phương thức display() để hiển thị thông
tin của một sinh viên:

#include<iostream>

using namespace std;

struct Student {
string name;
int dateOfBirth;
void display() {
cout << "Name: " << name << endl;
cout << "Age: " << dateOfBirth;
}
};

int main() {
Student s1;
s1.name = "Codelearn";
s1.dateOfBirth = 2019;
s1.display();
return 0;
}

Kết quả khi chạy chương trình:

Name: Codelearn
Age: 2019

Có thể thấy phương thức display() có thể truy xuất tới thuộc tính name và thuộc
tính dateOfBirth.
Đọc tới đây bạn đã có biết cách tạo và sử dụng phương thức bên trong struct, hãy quay lại
phần bài tập và làm thử.

Hướng dẫn
Code mẫu:

#include<iostream>

using namespace std;

struct Student {
string name;
int age;
void display() {
cout << "Name: " << name << endl;
cout << "Age: " << age << endl;
}
};

int main() {
int n;
cin >> n;
Student students[100];
for (int i = 0; i < n; i++) {
cin >> students[i].name >> students[i].age;
}
for (int i = 0; i < n; i++) {
students[i].display();
}
return 0;
}

Bài tập
Bạn hãy hoàn thiện hàm getInformation() và display() để chương trình nhập và hiển thị ra
thông tin của n sinh viên giống bài trước.
Ví dụ nếu bạn nhập

2
Binh 19
An 20

Thì màn hình sẽ hiển thị ra:

Name: Binh
Age: 19
Name: An
Age: 20

Hướng dẫn
Code mẫu:

#include<iostream>
using namespace std;

struct Student {
string name;
int age;
void display() {
cout << "Name: " << name << endl;
cout << "Age: " << age << endl;
}
void getInformation() {
cin >> name >> age;
}
}Student;

int main() {
int n;
Student students[100];
cin >> n;
for (int i = 0; i < n; i++) {
students[i].getInformation();
}
for (int i = 0; i < n; i++) {
students[i].display();
}
return 0;
}
Mảng : [Cpp Advanced] array1
Bài tập
Cho mảng số nguyên n phần tử arr được nhập từ bàn phím, bạn hãy viết hàm trả về phần
tử lớn nhất trong mảng arr sử dụng con trỏ và hiển thị ra màn hình:

Max value = {P}

Với {P} là giá trị của phần tử lớn nhất trong mảng.

Lý thuyết
Bài này sẽ giúp bạn hiểu hơn về mảng.
Tên mảng (chính là biến arr trong bài này) mà từ trước tới nay bạn sử dụng có bản chất
giống như một con trỏ. Để hiểu rõ hơn bạn có thể xem ví dụ sau:

#include <iostream>

using namespace std;

int main() {
int arr[] = { 1, 2, 3, 4, 5 };
// Hiển thị giá trị của arr
cout << arr << endl;
// Hiển thị địa chỉ của phần tử đầu tiên trong mảng
cout << &arr[0];
return 0;
}

Kết quả khi chạy chương trình

0039FA9C
0039FA9C

Có thể thấy giá trị của arr chính là địa chỉ của phần tử đầu tiên trong mảng (arr trỏ tới địa
chỉ của arr[0]), nên có thể coi arr giống như 1 con trỏ.
Lưu ý: arr không phải là một con trỏ mà arr được dùng giống như một con trỏ.
Bạn có thể duyệt mảng bằng biến arr mà biến arr giống như một con trỏ nên bạn cũng có
thể duyệt mảng bằng 1 con trỏ. Xem ví dụ sau:

#include <iostream>

using namespace std;

int main() {
int arr[] = { 1, 2, 3, 4, 5 };
int* p = &arr[0];
for (int i = 0; i < 5; i++) {
cout << p[i] << " ";
}
return 0;
}

Kết quả khi chạy chương trình:

1 2 3 4 5

Có thể thấy bạn có thể sử dụng con trỏ p giống như sử dụng biến arr. Đọc tới đây bạn đã
biết cách sử dụng con trỏ để duyệt mảng, hãy quay lại phần bài tập và làm thử.

Hướng dẫn
Code mẫu:

#include <iostream>

using namespace std;

int getMaxValue(int* p, int n) {


int maxValue = p[0];
for (int i = 0; i < n; i++) {
if (p[i] > maxValue) {
maxValue = p[i];
}
}
return maxValue;
}

int main() {
int n;
int arr[100];
cin >> n;
for (int i = 0; i < n; i++) {
cin >> arr[i];
}
cout << "Max value = " << getMaxValue(&arr[0], n);
return 0;
}

Bạn có thể truyền thẳng biến arr vào hàm getMaxValue() thay vì truyền &arr[0] vì arr chính
là &arr[0]:

#include <iostream>

using namespace std;

int getMaxValue(int* p, int n) {


int maxValue = p[0];
for (int i = 0; i < n; i++) {
if (p[i] > maxValue) {
maxValue = p[i];
}
}
return maxValue;
}
int main() {
int n;
int arr[100];
cin >> n;
for (int i = 0; i < n; i++) {
cin >> arr[i];
}
cout << "Max value = " << getMaxValue(arr, n);
return 0;
}

Bài tập
Cho mảng các số nguyên n phần tử được nhập từ bàn phím (mảng này được cấp phát
động và quản lý thông qua con trỏ arr), bạn hãy viết chương trình tính tổng của các phần
tử trong mảng và hiển thị ra màn hình:

Sum = {P}

Với {P} là tổng của các phần tử trong mảng.


Nếu bạn chưa biết về khái niệm cấp phát bộ nhớ động thì có thể xem phần lý thuyết bên
dưới.

Lý thuyết
Trước hết bạn hãy xem lại cách khai báo mảng thông thường:

// Khai báo mảng a 100 phần tử


int arr[100];

Nhược điểm:

 Với cách khai báo này số phần tử của mảng luôn phải cố định, bạn không thể thay
đổi kích thước của mảng khi chương trình đang chạy. Ví dụ vì lý do nào đó mà sau
này mảng arr cần tới 101 phần tử thì bạn chỉ có cách tắt chương trình đi và code lại
thành int arr[101]; (do mảng không tự tăng kích thước được).
 Mảng sẽ được cấp phát trên vùng nhớ Stack, và kích thước của vùng nhớ này là
nhỏ nên bạn không thể khai báo mảng có nhiều phần tử được. Ví dụ bạn hãy thử
chạy chương trình sau:

 #include <iostream>

 using namespace std;

 int main() {
 int arr[10000000];
 return 0;
 }

Chương trình này sẽ báo lỗi do kích thước của vùng nhớ Stack không đủ để cấp
phát bộ nhớ cho mảng a.
Ưu điểm:

 Mảng được cấp phát trên vùng nhớ Stack nên sẽ có tốc độ truy xuất nhanh (vùng
nhớ stack tuy có kích thước nhỏ nhưng lại có tốc độ truy xuất nhanh hơn các vùng
nhớ khác).
 Bộ nhớ được chương trình quản lý tự động (vùng nhớ được cấp phát cho mảng sẽ
được chương trình giải phóng khi không dùng nữa).

Mảng được cấp phát động sẽ giải quyết được những nhược điểm trên nhưng cũng có một
số nhược điểm. Trước hết bạn hãy xem cách để cấp phát bộ nhớ động cho mảng:

// Cấp phát bộ nhớ động cho mảng có 10000000 phần tử


// Để cấp phát bộ nhớ động cho mảng ta sử dụng toán tử new
int* arr = new int[10000000];

Đoạn code trên cấp phát bộ nhớ động cho mảng có 10000000 phần tử, lưu ý rằng arr không
phải là một mảng, arr là một con trỏ trỏ tới vùng nhớ được cấp phát động (trỏ tới phần tử
đầu tiên của mảng được cấp phát động) hay hiểu cách khác arr là con trỏ dùng để quản lý
mảng có 10000000 phần tử.
Bạn có thể sử dụng mảng được cấp phát động giống như mảng thông thường:
#include <iostream>

using namespace std;

int main() {
// Cấp phát động cho mảng có 10 phần tử
int* arr = new int[10];
for (int i = 0; i < 10; i++) {
arr[i] = i;
}
for (int i = 0; i < 10; i++) {
cout << arr[i] << " ";
}
// Giải phóng bộ nhớ được cấp phát động khi không sử dụng
delete[]arr;
return 0;
}

Kết quả khi chạy chương trình:

0 1 2 3 4 5 6 7 8 9

Ưu điểm của cấp phát bộ nhớ động

 Số phần tử của mảng không nhất thiết phải cố định. Ví dụ:

 #include <iostream>

 using namespace std;

 int main() {
 int n;
 cin >> n;
 // Cấp phát động cho mảng có n phần tử
 int* arr = new int[n];
 // Giải phóng bộ nhớ được cấp phát động khi không sử dụng
 delete[]arr;
 return 0;
 }

Có thể thấy trong ví dụ trên số phần tử của mảng được cấp phát động là một biến
được nhập từ bàn phím.

 Bạn có thể thay đổi kích thước mảng khi chương trình đang chạy (bản chất là cho
con trỏ tới vùng nhớ khác, ví dụ nếu bạn cần tăng kích thước của mảng thì bạn chỉ
cần cho con trỏ trỏ tới vùng nhớ có kích thước lớn hơn).
 Mảng được cấp phát trên vùng nhớ Heap mà vùng nhớ Heap là vùng nhớ có kích
thước lớn nhất trong các vùng nhớ nên với cấp phát động bạn có thể khai báo mảng
với nhiều phần tử. Ví dụ:

 #include <iostream>

 using namespace std;

 int main() {
 int* arr = new int[10000000];
 // Giải phóng bộ nhớ được cấp phát động khi không sử dụng
 delete[]arr;
 return 0;
 }

Chương trình này khi chạy sẽ không báo lỗi do vùng nhớ Heap có kích thước lớn và
có thể lưu trữ được mảng có 10000000 phần tử.

Nhược điểm của cấp phát bộ nhớ động


 Vùng nhớ Heap có tốc độ truy xuất chậm hơn vùng nhớ Stack nên việc truy xuất vào
các phần tử trong mảng được cấp phát động sẽ chậm hơn.
 Bạn phải tự quản lý bộ nhớ được cấp phát động (chương trình sẽ không quản lý cho
bạn). Nếu bạn thực hiện cấp phát bộ nhớ động mà không giải phóng thì vùng nhớ
này sẽ vẫn còn cho tới khi khởi động lại máy (gây lãng phí bộ nhớ, tệ hơn nữa là
tràn bộ nhớ).

Để giải phóng bộ nhớ được cấp phát động bạn dùng toán tử delete[]. Ví dụ:

#include <iostream>

using namespace std;

int main() {
// Cấp phát bộ nhớ động cho mảng có 1000 phần tử
int* arr = new int[1000];
...
// Giải phòng bộ nhớ khi không dùng tới
delete[] arr;
return 0;
}

Do đó khi cấp phát động hãy luôn nhớ phải dùng toán tử delete[] để giải phóng bộ nhớ khi
không dùng tới nữa.
Lỗi thường gặp khi cấp phát bộ nhớ động:

#include<iostream>

using namespace std;

int main() {
int* arr = new int[1000];
arr = new int[2000];
delete[]arr;
return 0;
}

Đoạn code trên sẽ gây ra rò rỉ bộ nhớ do khi gán arr = new int[2000]; thì vùng nhớ được
cấp phát động cho mảng có 1000 phần tử vẫn còn đó, bạn cần giải phóng vùng nhớ này
trước khi cho arr trỏ tới vùng nhớ mới giống như sau:

#include<iostream>

using namespace std;

int main() {
int* arr = new int[1000];
delete[]arr;
arr = new int[2000];
delete[]arr;
return 0;
}

Đọc tới đây bạn đã hiểu về cấp phát bộ nhớ động và biết cách sử dụng mảng được cấp
phát bộ nhớ động, hãy quay lại phần bài tập và làm thử.
Nếu bạn chưa làm được bài này thì có thể xem hướng dẫn ở bên dưới.

Hướng dẫn
Code mẫu:

#include <iostream>

using namespace std;

int main() {
int n;
int* arr;
cin >> n;
// Thực hiện cấp phát bộ nhớ động cho mảng
arr = new int[n];
// Nhập dữ liệu cho mảng
for (int i = 0; i < n; i++) {
cin >> arr[i];
}
// Tính tổng các phần tử trong mảng và lưu vào biến sum
int sum = 0;
for (int i = 0; i < n; i++) {
sum += arr[i];
}
cout << "Sum = " << sum;
delete[]arr;
return 0;
}

Bài tập
Cho mảng các số nguyên n phần tử được nhập từ bàn phím (mảng này được cấp phát
động và quản lý thông qua con trỏ arr), bạn hãy viết chương trình hiển thị ra những số chia
hết cho 3 và không chia hết cho 5 trong mảng.
Ví dụ nếu bạn nhập n = 5, arr = [6, 15, 3, 7, 30] giống như bên dưới:

5
6 15 3 7 30

Thì màn hình sẽ hiển thị ra:

6 3

Hướng dẫn
Code mẫu:

#include <iostream>
using namespace std;

int main() {
int n;
int* arr;
cin >> n;
// Thực hiện cấp phát bộ nhớ động cho mảng
arr = new int[n];
// Nhập dữ liệu cho mảng
for (int i = 0; i < n; i++) {
cin >> arr[i];
}
// Hiển thị ra màn hình các số chia hết cho 3 và không chia hết
cho 5 trong arr
for (int i = 0; i < n; i++) {
if (arr[i] % 3 == 0 && arr[i] % 5 != 0) {
cout << arr[i] << " ";
}
}
delete[]arr;
return 0;
}

You might also like