You are on page 1of 16

9/24/2018

Nội dung

1. Biến
CẤU TRÚC DỮ LIỆU VÀ GIẢI 2. Hàm
THUẬT
3. Mảng
Data Structures & Algorithms
ÔN TẬP NHẬP MÔN LẬP TRÌNH 4. Cấu trúc

5. Con trỏ

Ô nhớ & Vùng nhớ


Cấu trúc một chương trình C/C++ • Bộ nhớ máy tính
Địa chỉ 1 byte
• Bộ nhớ RAM chứa rất nhiều ô nhớ, mỗi ô
ô nhớ
nhớ có kích thước 1 byte. 0
#include <iostream> 1
int main () • Mỗi ô nhớ có địa chỉ duy nhất và địa chỉ 2
{ này được đánh số từ 0 trở đi.
std::cout<<"Xin chao";
3
return 0; • RAM để lưu trữ mã chương trình và dữ
4
} 5
liệu trong suốt quá trình thực thi. 6
7
. .
. .
. .

Memory Layout (bytes)
3 4

1. Biến 1. Biến

- Biến là một ô nhớ hoặc 1 vùng nhớ dùng để chứa dữ liệu  Cách khai báo:
trong quá trình thực hiện chương trình
type variableNames;
- Mỗi biến có một kiểu dữ liệu cụ thể, kích thước của biến − type: là một trong các kiểu dữ liệu hợp lệ.
phụ thuộc vào kiểu dữ liệu.
− variableNames: tên của một hay nhiều biến phân
- Giá trị của biến có thể được thay đổi, tất cả các bài phải cách nhau bởi dấu phẩy.
khởi tạo trước khi sử dụng
int so_nguyen; float mark1;
- Qui cách đặt tên biến: float so_thuc;
int x;
• Không trùng với các từ khóa, hoặc tên hàm.
• Ký tự đầu tiên là chữ cái hoặc _
• Không được sử dụng khoảng trắng ở giữa các ký tự
• Nên sử dụng tất cả chữ thường với dấu _ giữa các từ.

1
9/24/2018

1. Biến 1. Biến - địa chỉ biến

•Mỗi 1 biến khi được khai báo sẽ được cấp 1 vùng nhớ với
 Ngoài ra, ta có thể vừa khai báo vừa khởi tạo địa chỉ duy nhất để lưu trữ biến đó.
giá trị ban đầu cho biến:
•Để truy cập vào địa chỉ của một biến ta sử dụng câu lệnh.
type varName1=value, ... ,varName_n=value; &
 Ví dụ:
float mark1, mark2, mark3, average = 0;
#include <iostream>
int main() 008FF82C
{ a 5
int a = 5;
std::cout<<"Gia tri cua a: "<<a<<'\n';
std::cout<<"Dia chi cua a: "<<&a<<'\n'; Gia tri cua a: 5
return 0; Dia chi cua a: 008FF82C
}
8

1. Biến – biến cục bộ


1. Biến - địa chỉ biến
- Biến được định nghĩa trong một hàm hoặc 1 block
được gọi là biến cục bộ
Mọi biến đều có 2 thông tin: Giá trị và địa chỉ - Biến cục bộ chỉ được sử dụng bên trong hàm hoặc
block
- Các hàm bên ngoài khác sẽ không truy cập được biến
int x=5; cục bộ
cout<<“Giá trị của x=”<<x; #include <iostream> Biến a, b, c là các biến cục bộ
int main()
{ bên trong hàm main
cout<<“Địa chỉ của x=”<<&x; int a = 2, b = 3;
int c;
c = a + b;
std::cout<<c;
return 0;
}

9 10

1. Biến – biến toàn cục Biến cục bộ VS biến toàn cục


- Biến toàn cục được định nghĩa bên ngoài các hàm, và
thường được định nghĩa ở phần đầu của source code file. - Khi biến cục bộ được định nghĩa, giá trị của biến sẽ không
được khởi tạo.
- Biến toàn cục sẽ giữ giá trị của biến xuyên suốt chương
trình.  Ta phải gán giá trị cho biến cục bộ để khởi tạo
- Tất cả các hàm đều có thể truy cập biến toàn cục
- Biến toàn cục sẽ được tự động khởi tạo giá trị

Ví dụ 1 Ví dụ 2
#include <iostream> #include <iostream> Kiểu dữ liệu Giá trị khởi tạo
int g; int g = 20;
int main() int main()
int 0
{ { char ‘\0’
int a = 2, b = 3; int a = 2, b = 3;
g = a + b; g = a + b; float 0
std::cout<<g; std::cout<<g;
return 0; return 0;
double 0
} } pointer NULL
11 12
g = ??? g = ???

2
9/24/2018

1. Biến – Hằng 1. Biến – Hằng

Cách định nghĩa hằng trong C++: Có 2 cách Cách định nghĩa hằng trong C++: Có 2 cách
Cách 1: Sử dụng #define
Cách 2: Sử dụng const
Câu lệnh:
#define tên_hằng giá_trị Lưu ý: Không có ký tự ; Câu lệnh:
Ví dụ:
Ví dụ: const kiểu_giá_trị tên_hằng = giá_trị_hằng;

#include <iostream>
int main()
#include <iostream>
{
#define PI 3.14
const int PI = 3.14;
int main()
int r = 2;
{
std::cout<<r*PI;
int r = 2;
getch();
std::cout<<2*r*PI;
return 0;
return 0;
}
}
6.28 6.28
13

1. Biến – Và Vùng Nhớ 1. Toán tử * và & với Biến - Vùng Nhớ

Địa chỉ
ô nhớ • Toán tử & (Address-of Operator) đặt trước tên biến và
0
ch cho biết địa chỉ của vùng nhớ của biến.
1 x
2 • Toán tử * (Dereferencing Operator hay Indirection
3 a
int main() { Operator) đặt trước một địa chỉ và cho biết giá trị lưu trữ
4
char ch=‘x’; 5 7 tại địa chỉ đó.
int a = 7; 6
• Ví dụ:
} 7
. .
. .
. .

Memory Layout (bytes) 15

2. Hàm
int value; 0x50
value = 3200; value 3200  Hàm là một khối lệnh thực hiện một công việc hoàn
chỉnh (module), được đặt tên và được gọi thực thi nhiều
Memory Layout
lần tại nhiều vị trí trong chương trình.
cout << " value = " << value;
=> value = 3200;  Hàm còn gọi là chương trình con (subroutine)

 Hàm có thể được gọi từ chương trình chính (hàm main)


cout << " &value = " << &value;
hoặc từ 1 hàm khác.
=> &value = 0x50;
 Hàm có giá trị trả về hoặc không. Nếu hàm không có
cout << " *(&value) = " << *(&value); giá trị trả về gọi là thủ tục (procedure)
=> *(&value) = 3200;

3
9/24/2018

2. Hàm 2. Hàm

• Một đoạn chương trình có tên, đầu vào và đầu ra.


• Dạng tổng quát của hàm do người dùng định
• Có chức năng giải quyết một số vấn đề chuyên nghĩa:
biệt cho chương trình chính. returnType functionName(parameterList)
• Được gọi nhiều lần với các tham số khác nhau. {
• Được sử dụng khi có nhu cầu: body of the function
return value
• Tái sử dụng.
}
• Sửa lỗi và cải tiến.

Tham số và đối số 2. Hàm

• Parameter
• tạm dịch: Tham số hoặc tham số hình thức
• Là các thông số mà hàm nhận vào
• Xác định khi khai báo hàm
Gọi hàm
• Argument
• Tạm dịch: Đối số hoặc Tham số thực sự Truyền đối số

• Là các thông số được đưa vào hàm khi tiến hành gọi hàm

• Hai thuật ngữ này đôi khi dùng lẫn lộn và gọi chung
là Tham số
Tham số

22

2. Hàm Ví dụ
• Hàm có đầu ra, không có đầu vào:
• Tên hàm: nhap_so_duong , Hàm yêu cầu người dùng
Nội dung nhập vào một số nguyên dương. Nếu không phải số
Tên hàm Đầu vào Đầu ra
của hàm dương yêu cầu nhập lại.
• Chức năng • Số lượng • Kiểu dữ • Các lệnh • Đầu vào: Không có
của hàm tham số, liệu đầu ra cần thiết để • Đầu ra: số nguyên dương.
kiểu dữ liệu • Một số hàm hàm thực
• Một số hàm không có hiện công int nhap_so_duong(){
không có đầu ra việc int n;
đầu vào (void) do {
cout << "Nhap mot so nguyen duong";
cin >> n;
} while (n <= 0);
return n;
}

4
9/24/2018

Ví dụ Ví dụ

• Hàm có đầu vào, không có đầu ra: • Hàm không có đầu vào lẫn đầu ra
• Tên hàm: xuat_so_lon, Xuất ra màn hình số lớn hơn • Tên hàm: nhap_xuat_so_lon, Yêu cầu nhập
trong 02 số. vào 02 số nguyên và xuất ra màn hình ước
• Đầu vào: Hai số nguyên. Đặt tên là a và b
chung lớn nhất của 02 số đó.
• Đầu vào: Không có
• Đầu ra: Không có
• Đầu ra: Không có
void xuat_so_lon(int a, int b){
int m; void nhap_xuat_so_lon(){
if (a > b) m = a; int m, n;
else m = b; cout << "Nhap so nguyen duong"; cin >> m;
} cout << "Nhap so nguyen duong"; cin >> n;
cout << “so lon nhat giua " cout << “So lon hon trong "
<< a << " va " << b << " la " << m; << m << " va " << n << " la ";
} if (n > m) m = n;
cout << m;
}

Ví dụ Trả về giá trị


• Hàm có cả đầu vào và đầu ra • Lệnh return dùng để trả về giá trị đầu ra của
• Tên hàm: so_lon, Nhận vào 02 số nguyên
dương và trả về số lớn hơn trong 02 số đó. hàm
• Đầu vào: Hai số nguyên dương, đặt tên m • Hàm chỉ trả về được duy nhất 01 giá trị. Lệnh
và n
• Đầu ra: Số nguyên dương có giá trị lớn hơn return sẽ kết thúc quá trình thực thi của hàm
trong m và n

int so_lon(int m, int n){


int so_lon(int m, int n){ if (n > m) return n;
if (n > m) m = n; return m;
return m; }
}

28

Thuật ngữ - truyền đối số


Trả về giá trị
• Các hàm không có đầu ra sẽ có kiểu trả về là void • Truyền đối số - to pass argument – là công việc
• Không có biến kiểu void đưa các thông số cho hàm hoạt động khi gọi
• Lệnh return với các hàm không có đầu ra sẽ không kèm hàm.
theo giá trị (nhưng vẫn sẽ kết thúc việc thực thi hàm)
• Đối số phải được truyền tương ứng với cách tham
void xuat_so_lon(int a, int b){ số đã được khai báo.
cout << “so lon nhat giua "
<< a << " va " << b << " la " ; • Có 02 cách truyền đối số chính
if (a > b) {
cout << a; • Pass by value – Truyền giá trị (truyền tham trị)
return;
} • Pass by reference – Truyền tham chiếu
cout << b;
}

29

5
9/24/2018

Truyền giá trị


Truyền giá trị
int so_lon(int m, int n){
• Là cách mặc định của C/C++ if (n > m) m = n;
• Tham số chứa bản sao giá trị của đối số. Thay đổi return m;
int so_lon(m, n)
}
tham số không ảnh hưởng đến đối số. int main()
{ 8 36
int m = 8, n = 36;
int o = so_lon(m, n); m n
int so_lon(int m, int n){
if (n > m) m = n; cout << "UCLN cua " << m
return m; << " va " << n << " la " << o; 36
8
} }
int main()
{
int m = 8, n = 36;
• Truyền giá trị tạo ra bản sao của int main()
int o = so_lon(m, n);
đối số và lưu vào trong vùng nhớ
cout << "UCLN cua " << m << " va " << n << " la " << o; của tham số 8 36
}
m n

32

Truyền giá trị Truyền giá trị


int so_lon(int m, int n){
• Có thể truyền đối số là bất cứ cú pháp nào tính if (n > m) m = n;
return m;
được thành giá trị (hằng, biến, biểu thức, lời gọi, }
int nhap_so_duong(){
v.v...) int n;
do {
int so_lon(int m, int n){ cout << "Nhap mot so nguyen duong";
cin >> n;
if (m > n) n = m; } while (n <= 0);
return n; return n;
}
} int main()
int nhap_so_duong(){ {
int n; cout << "Nhap mot so nguyen "; cin >> n; cout << “so lon nhat trong 04 so la "
<< so_lon(
return n; so_lon(nhap_so_duong(), nhap_so_duong())
} , so_lon(nhap_so_duong(), nhap_so_duong())
);
int main() }
{
cout << “So lon hon la " << so_lon(9*4, nhap_so_duong());
}
• Giải quyết vấn đề đặt ra ở đầu bài.

Truyền tham chiếu Truyền tham chiếu


void hoan_vi(int& a, int& b){
• Áp dụng cho các tham số khi khai báo có dấu
int c = a; Output:
& phía sau kiểu dữ liệu. a = b; 5 3
b = c; 3 5

• Chỉ có thể truyền các đối số là biến (hoặc hằng }


int main()
nếu tham số khai báo là const)
{
• Các tham số là tham chiếu không được cấp phát int a, b;
cin >> a >> b;
vùng nhớ hoan_vi(a, b);
• Tham số được truyền tham chiếu sẽ trỏ đến cùng địa cout << a << " " << b;
}
chỉ vùng nhớ của đối số truyền cho nó • Chương trình xuất ra hai số ngược với
• Tham số sẽ trở thành một ánh xạ đến đối số. Mọi thay thứ tự chúng được nhập vào
đổi lên tham số sẽ thay đổi luôn đối số. • Đối số truyền vào bắt buộc phải là biến,
không thể dùng hàm nhap_so_duong
trong trường hợp này

6
9/24/2018

Truyền tham chiếm Truyền tham chiếu


void hoan_vi(int& a, int& b){ bool phep_chia(int x, int y, double& thuong){
int c = a; if (y != 0) {
a = b; thuong = double(x)/y;
int hoan_vi(a, b) return true;
b = c; } else {
} return false;
int main() a b }
{ }
int main(){
int m, n; double thuong;
cin >> m >> n; if (phep_chia(5, 3, thuong)){
hoan_vi(m, n); cout << "Thuong so la " << thuong;
cout << m << " " << n; } else {
cout << "Khong the chia duoc ";
} }
int main()
}
• Truyền tham chiếu liên kết 8 36 • Dùng truyền tham chiếu như một cách trả về kết quả
tham số đến vùng nhớ của
đối số. Tham số không có m n
vùng nhớ
37

3. Mảng 3. Mảng

• Biểu diễn một dãy các phần tử có cùng kiểu và mỗi phần tử trong • Mảng 1 chiều gồm 1 dãy các phần tử có cùng kiểu dữ liệu (int, float,
mảng biểu diễn 1 giá trị. char …) 5 8 2 7 1 0 9

• Kích thước mảng được xác định ngay khi khai báo và không thay T B R K

đổi.
• Một kiểu dữ liệu có cấu trúc do người lập trình định nghĩa. • Mảng 2 chiều (Ma trận) gồm các phần tử trên dòng và các phần tử
• Ngôn ngữ lập trình C/C++ luôn chỉ định một khối nhớ liên tục cho trên cột 3 7
một biến kiểu mảng. 3 7 3 7 8 6 1

Ví dụ: dãy các số nguyên, dãy các ký tự… 6 1 6 1 4 6 1

0 1 2 3 4 5 6 7 8 9 Ma trận dòng = cột = 2 Ma trận dòng < cột Ma trận dòng > cột
Dòng = 2 , cột =3 Dòng = 3 , cột =2
A B C D E F G
39 40

3. Mảng một chiều


• Cú pháp: • Phải xác định cụ thể <số phần tử mảng> ngay lúc khai báo,
không được sử dụng biến hoặc hằng thường.
<Kiểu dữ liệu> <Tên biến mảng>[<Số phần tử mảng>];
Trong đó:
int n1 = 10; int a[n1];
Kiểu dữ liệu: int, float, char
Tên biến mảng: 1 ký tự hoặc 1 dãy ký tự viết liền nhau và không có const int n2 = 20; int b[n2];
khoảng trắng
Số phần tử mảng: số lượng các phần tử của mảng 1 chiều
• Nên sử dụng chỉ thị tiền xử lý #define để định nghĩa số phần tử
mảng

char A[10] int Mang1Chieu[30] #define n1 10


Kiểu dữ liệu: char Kiểu dữ liệu: int #define n2 20
Tên biến mảng: A Tên biến mảng: Mang1Chieu int a[n1]; // int a[10];
Số phần tử mảng: 10 phần tử Số phần tử mảng: 30 phần tử int b[n1][n2]; // int b[10][20];
41 42

7
9/24/2018

• Khởi tạo giá trị cho mọi phần tử của mảng • Chỉ số mảng (vị trí trong mảng) là một giá trị số nguyên int.
0 1 2 3
int A[4] = {29, 137, 50, 4}; • Chỉ số bắt đầu là 0 và không vượt quá số lượng phần tử tối đa
29 137 50 4
trong mảng.
• Khởi tạo giá trị cho một số phần tử đầu mảng
• Số lượng các chỉ số mảng = số lượng phần tử tối đa trong mảng
int B[4] = {91, 106}; 0 1 2 3

91 106
• Khởi tạo giá trị 0 cho mọi phần tử của mảng 0 1 2 3 4
int A[5]; 99 17 50 43 72
int a[4] = {0}; 0 1 2 3
0 0 0 0 Tên mảng: A
• Tự động xác định số lượng phần tử Kiểu dữ liệu của từng phần tử trong mảng: int
int a[] = {22, 16, 56, 19}; Số phần tử tối đa trong mảng: 5 phần tử
0 1 2 3 Các chỉ số được đánh số: 0  4 (0, 1, 2, 3, 4)
22 16 56 19
43 44

3. Mảng một chiều


• Truy xuất phần tử mảng thông qua chỉ số
<Tên biến mảng>[<chỉ số mảng>]
• Cú pháp:
• Các phần tử mảng là 1 dãy liên tục có chỉ số từ 0 đến <Số phần tử
&<Tên biến mảng>[<chỉ
0 1 2số mảng>];
3
mảng>-1
int A[4] 29 137 50 4

0 1 2 3 Địa chỉ các phần tử mạng:


int A[4] 29 137 50 4
Địa chỉ phần tử thứ 0: &A[0]
Các truy xuất hợp lệ: A[0], A[1], A[2], A[3] Địa chỉ phần tử thứ 1: &A[1]
Các truy xuất không hợp lệ: A[-1], A[4], A[5] Địa chỉ phần tử thứ 2: &A[2]
Giá trị các phần tử mảng A[0]=29, A[1]=137, A[2]=50, A[3]=4 Địa chỉ phần tử thứ 3: &A[3]

45 46

3. Mảng hai chiều


0 1 2 3
• Cú pháp:
0 29 137 50 4
<Kiểu dữ liệu> <Tên biến mảng>[<Số Dòng>][<Số Cột>]; int A[2][4]
1 5 32 657 97
Trong đó:
Kiểu dữ liệu: int, float, char 0 1
int B[2][2]
Tên biến mảng: 1 ký tự hoặc 1 dãy ký tự viết liền nhau và không có 0 29 137
khoảng trắng 1 5 32
Dòng, Cột: số lượng các phần tử mỗi chiều của mảng int C[2][1] 0
0 29
char A[10][20] int Mang2Chieu[3][5]
1 5
Kiểu dữ liệu: char Kiểu dữ liệu: int
Tên biến mảng: A Tên biến mảng: Mang2Chieu
Mảng có 10 dòng và 20 cột Mảng có 3 dòng và 5 cột
47 48

8
9/24/2018

• Truy xuất phần tử mảng thông qua chỉ số


• Chỉ số mảng là một giá trị số nguyên int.
<Tên biến mảng>[<Chỉ số dòng>][<Chỉ số cột>]
• Chỉ số trong mảng 2 chiều gồm chỉ số dòng và chỉ số cột.
• 0 ≤ chỉ số dòng ≤ số dòng của mảng - 1 0 1 2
29 137 50 0
int A[2][3]
• 0 ≤ chỉ số cột ≤ số cột của mảng - 1
3 78 943 1

0 1 2 Các truy xuất hợp lệ: A[0][0], A[0][1],…, A[1][2], A[1][3]


0 2 45 7
int A[2][3]; Các truy xuất không hợp lệ: A[-1][0], A[1][4], A[2][0]
1 73 11 187
Tên mảng: A Giá trị các phần tử mảng:
Kiểu dữ liệu của từng phần tử trong mảng: int A[0][0]=29, A[0][1]=137, A[0][2]=50
Số phần tử tối đa trong mảng: 2*3=6 phần tử A[1][0]=3, A[1][1]=78, A[1][2]=943
Các chỉ số được đánh số: Chỉ số dòng: 0, 1
Chỉ số Cột: 0, 1, 2 49 50

4. Struct 4. Struct – Khai báo biến cấu trúc


• Cú pháp không tường minh
• Cú pháp tường minh
struct <tên kiểu cấu trúc>
struct <tên kiểu cấu trúc> {
{ <kiểu dữ liệu> <tên thành phần 1>;
<kiểu dữ liệu> <tên thành phần 1>; …
… <kiểu dữ liệu> <tên thành phần n>;
<kiểu dữ liệu> <tên thành phần n>; };
} <tên biến 1>, <tên biến 2>; struct <tên kiểu cấu trúc> <tên biến>;
• Ví dụ
• Ví dụ

struct DIEM struct DIEM


{ {
int x; int x;
int y; int y;
} diem1, diem2; };
struct DIEM diem1, diem2;// C++ có thể bỏ struct

4. Struct – Khai báo biến cấu trúc typedef


Khởi tạo cho biến cấu trúc
• Cú pháp • Cú pháp tường minh
typedef struct
{ struct <tên kiểu cấu trúc>
<kiểu dữ liệu> <tên thành phần 1>; {
… <kiểu dữ liệu> <tên thành phần 1>;
<kiểu dữ liệu> <tên thành phần n>; …
} <tên kiểu cấu trúc>; <kiểu dữ liệu> <tên thành phần n>;
<tên kiểu cấu trúc> <tên biến>; } <tên biến> = {<giá trị 1>,…,<giá trị n>};

• Ví dụ • Ví dụ
typedef struct struct DIEM
{ {
int x; int x;
int y; int y;
} DIEM; } diem1 = {2912, 1706}, diem2;
struct DIEM diem1, diem2;

9
9/24/2018

Truy xuất dữ liệu kiểu cấu trúc Gán dữ liệu kiểu cấu trúc
• Đặc điểm • Có 2 cách

• Không thể truy xuất trực tiếp <biến cấu trúc đích> = <biến cấu trúc nguồn>;
• Thông qua toán tử thành phần cấu trúc . hay còn gọi là toán tử chấm
<biến cấu trúc đích>.<tên thành phần> = <giá trị>;
(dot operation)
• Ví dụ
<tên biến cấu trúc>.<tên thành phần>
• Ví dụ struct DIEM
{
int x, y;
struct DIEM } diem1 = {2912, 1706}, diem2;
{ …
int x; diem2 = diem1;
int y; diem2.x = diem1.x;
} diem1; diem2.y = diem1.y * 2;
printf(“x = %d, y = %d”, diem1.x, diem1.y);

Cấu trúc phức tạp Cấu trúc phức tạp


• Thành phần của cấu trúc là cấu trúc khác • Thành phần của cấu trúc là mảng

struct DIEM struct SINHVIEN


{ {
int x; char hoten[30];
int y; float toan, ly, hoa;
}; } sv1;

struct HINHCHUNHAT strcpy(sv1.hoten, “Nguyen Van A”);
{ sv1.toan = 10;
struct DIEM traitren; sv1.ly = 6.5;
struct DIEM phaiduoi; sv1.hoa = 9;
} hcn1;

hcn1.traitren.x = 2912;
hcn1.traitren.y = 1706;

Cấu trúc phức tạp 5. Con trỏ


• Cấu trúc đệ quy (tự trỏ)
• Khái niệm:
struct PERSON
{ Con trỏ (Pointer) là một biến lưu trữ địa chỉ của một địa chỉ bộ
char hoten[30]; nhớ. Địa chỉ này thường là địa chỉ của một biến khác.
struct PERSON *father, *mother;
}; VD: Biến x chứa địa chỉ của biến y. Vậy ta nói biến x “trỏ tới” y.

• Phân loại con trỏ:


struct NODE
{ Con trỏ kiểu int dùng để chứa địa chỉ của các biến
int value;
struct NODE *pNext; kiểu int. Tương tự ta có con trỏ kiểu float, double, …
};

NMLT - Con trỏ cơ bản 60

10
9/24/2018

5. Con trỏ 5. Con trỏ

• Khai báo <kiểu dữ liệu> *<tên biến con trỏ>; Khai báo:
DataType * PointerVariable;
• Giống như mọi biến khác, biến con trỏ muốn sử dụng cũng cần phải được
khai báo. Cấp phát:
Memory Layout PointerVariable = new DataType;
• Ví dụ
0x50
Hủy bộ nhớ:
char char1;
char1 0x80 delete PointerVariable ;
int *ptrI; ptrI
float *ptrF; 0x10
ptrF
61 62

5. Con trỏ 5. Con trỏ

• Có thể gán biến con trỏ:


int *p;
int *p1, *p2;
p = new int; // allocate space for an int
p2 = p1;
*p = 100;
 Gán một con trỏ cho con trỏ khác
cout << "At " << p << " ";
 “Chỉ định p2 trỏ tới nơi mà p1 đang trỏ tới“
cout << "is the value " << *p << "\n";
delete p; :
Dễ bị lẫn với *p2 = *p1;

 Gán “giá trị trỏ bởi p1” cho “giá trị trỏ bởi p2”

63 64

5. Con trỏ và toán tử * ,& 5. Con trỏ NULL


• Khái niệm
• Toán tử & dùng trong khởi tạo giá trị cho con trỏ
• Con trỏ NULL là con trỏ không trỏ vào đâu cả.
<kiểu dữ liệu> *<tên biến con trỏ> = & <tên biến>;
• Khác với con trỏ chưa được khởi tạo.

0x34 int n;
• Ví dụ:
a 0x90
int a; int *p1 = &n;
int *ptr = &a; ptr 0x34
int *p2; // unreferenced local variable
Memory Layout
int *p3 = NULL;

? double a;
int *ptr = &a;
NULL
65 66

11
9/24/2018

5. Con trỏ và toán tử * ,& 5. Con trỏ và các lưu ý

• Toán tử * đặt trước biến con trỏ cho phép truy xuất đến giá trị ô • Con trỏ là khái niệm quan trọng và khó nhất
nhớ mà con trỏ trỏ đến. trong C++. Mức độ thành thạo C++ được đánh

• Ví dụ
giá qua mức độ sử dụng con trỏ.

int a = 1000;
0x34 • Nắm rõ quy tắc sau, ví dụ int a, int *pa = &a;
a 3201
3200
1000 • *pa và a đều chỉ nội dung của biến a.
int *ptr = &a;
cout << ptr << “ ” << *ptr; • pa và &a đều chỉ địa chỉ của biến a.
// a = 3200 0x90 • Không nên sử dụng con trỏ khi chưa được khởi
*ptr = 3200; ptr 0x34 tạo. Kết quả sẽ không lường trước được.
cout << *ptr;
int *pa; *pa = 1904; => sai
(*ptr) ++; Memory Layout
67 68

5. Con trỏ và cấp phát động 5. Con trỏ và cấp phát động
• Cú pháp khởi tạo giá trị con trỏ: <type> pointer = new <type>
(value)
• Vì con trỏ có thể tham chiếu tới biến nhưng không thực sự cần
• Ví dụ:
phải có định danh cho biến đó.
#include <iostream>
• Có thể cấp phát động cho biến con trỏ bằng toán tử new. Toán tử new
using namespace std;
sẽ tạo ra biến “không tên” cho con trỏ trỏ tới.

• Cú pháp: <type> *<pointerName> = new <type> int main() {


Ví dụ: int *ptr = new int; int *p;
o Tạo ra một biến “không tên” và gán ptr trỏ tới nó p = new int(99); // initialize with 99
o Có thể làm việc với biến “không tên” thông qua *ptr
cout << *p; // displays 99
return 0;
}
69 70

1. int *p1, *p2;


5. Con trỏ và cấp phát động
5. *p2 = 40;
p1 ? • Toán tử delete dùng để giải phóng vùng nhớ trong HEAP do con trỏ trỏ tới (con trỏ được cấp
p1
p2 ? 40 pháp bằng toán tử new). Cú pháp:
p2
2. int *p1 = new int; delete <pointerName>;

p1 6. p1 = new int; • Ghi chú: Sau khi gọi toán tử delete thì con trỏ vẫn trỏ tới vùng nhớ trước khi gọi hàm delete. Ta

p2 ? ? p1 ? gọi là “con trỏ lạc”. Ta vẫn có thể gọi tham chiếu trên con trỏ, tuy nhiên:

p2 40 • Kết quả không lường trước được


3. *p1 = 30; • Thường là nguy hiểm

p1 7. *p1 = 50;  Hãy tránh con trỏ lạc bằng cách gán con trỏ bằng NULL sau khi delete.

p2 ? 30 p1 50 • Ví dụ:

4. p2 = p1; p2 40
delete pointer;
p1 pointer = NULL;

p2 30
71 72

12
9/24/2018

5. Định nghĩa kiểu dữ liệu con trỏ 5. Con trỏ và hàm

• Có thể đặt tên cho kiểu dữ liệu con trỏ


• Con trỏ là kiểu dữ liệu hoàn chỉnh có thể dùng nó như các kiểu
• Để có thể khai báo biến con trỏ như các biến khác khác
• Loại bỏ * trong khai báo con trỏ • Con trỏ có thể là tham số của hàm
Ví dụ: typedef int* IntPtr;
• Có thể là kiểu trả về của hàm
- Định nghĩa một tên khác cho kiểu dữ liệu con trỏ
Ví dụ: int* findOtherPointer(int* p);
- Các khai báo sau tương đương:
Hàm này khai báo:
IntPtr p;
- Có tham số kiểu con trỏ trỏ tới int
int *p;
- Trả về biến con trỏ trỏ tới int

73 74

5. Con trỏ và cấp phát động 5. Con trỏ và cấp phát động

• Ví dụ:
• Hạn chế của mảng chuẩn
int a[10];
• Bắt buộc phải biết trước cần bao nhiều bộ nhớ lưu trữ => tốn bộ nhớ, không
typedef int* IntPtr;
thay đổi được kích thước, …
IntPtr p;
 a và p là các biến con trỏ.  Dùng Mảng động
• Phép gán hợp lệ p = a;
• p bây giờ sẽ trỏ tới nơi a trỏ, tức là tới phần tử đầu tiên của mảng a
• Mảng động
• Phép gán không hợp lệ a = p; • Kích thước không xác định ở thời điểm lập trình
• Bởi con trỏ mảng là con trỏ hằng. • Mà xác định khi chạy chương trình

75 76

5. Con trỏ và cấp phát động Xóa mảng động

• Cấp phát động cho biến con trỏ • Dùng toán tử delete[] để xóa mảng động.
• Sau đó dùng con trỏ như mảng chuẩn Ví dụ:
• Cú pháp: double *d = new double[10];
<type> <pointer> = new <type> [<number_of_elements>]
Ví dụ:
//... Processing
typedef double * doublePtr; delete[] d;
 Giải phóng tất cả vùng nhớ của mảng động này
doublePtr d;
 Cặp ngoặc vuông báo hiệu có mảng
d = new double[10];
 Nhắc lại: d vẫn trỏ tới vùng nhớ đó. Vì vậy sau khi delete, cần
 Tạo biến mảng cấp phát động d có 10 phần tử, gán d = NULL;
kiểu cơ sở là double.

77 NMLT - Con trỏ và cấp phát động 78

13
9/24/2018

Hàm trả về kiểu mảng Bài tập

• Ta không được phép trả về kiểu mảng trong hàm. • Hãy viết HÀM tạo mảng 1 chiều có n phần tử bằng cấp phát động.
Ví dụ:
int[] someFunction(); // Không hợp lệ! • Viết hàm xuất mảng 1 chiều đã tạo.

• Có thể thay bằng trả về con trỏ tới mảng có cùng kiểu cơ sở: • Viết hàm đếm số phần tử âm trong mảng 1 chiều.
int* someFunction(); // Hợp lệ!

NMLT - Con trỏ và cấp phát động 80

Lời giải Lời giải


#include <iostream>
using namespace std;
// Hàm xuất mảng
int* Input(int n){
int *p; void Output(int *p, int n) {
p = new int[n];
for (int i = 0; i < n; i++){
cout << "\n Xuat mang 1 chieu: ";
cin >> p[i]; for (int i = 0; i < n; i++) {
}
return p; cout << p[i] << " " ;
} }
int main() { }
int *arr, n;
cout << "Nhap n: ";
cin >> n;
arr = Input(n);
}

81 82

5. Mảng động hai chiều Bài tập

• Là mảng của mảng Tạo mảng 2 chiều bằng con trỏ.


• Sử dụng định nghĩa kiểu con trỏ giúp hiểu rõ hơn:
typedef int* IntArrayPtr;
IntArrayPtr *m = new IntArrayPtr[3];
 Tạo ra mảng 3 con trỏ
 Sau đó biến mỗi con trỏ này thành mảng 4 biến int
for (int i = 0; i < 3; i++)
m[i] = new int[4];
 Kết quả là mảng động 3 x 4

83 84

14
9/24/2018

Lời giải 5. Con Trỏ và Hàm Số


int main() { • Tham số của hàm là 1 biến con trỏ
int row=2, col=3; • Trường hợp thay đổi giá trị của đối số

// Cấp phát vùng nhớ


int **p = new int*[row]; void Hoanvi(int *x, int *y)
if (p == NULL) exit(1); {
for (int i = 0; i < row; i++) { int z = *x;
p[i] = new int[col]; *x=*y;
if (p[i] == NULL) exit(1); *y=z;
} }
void main()
// Giải phóng vùng nhớ {
for (int i = 0; i < row; i++) { int a=1,b=2;
delete[] p[i]; cout<<a<<b; // 1 2
} Hoanvi(&a,&b);
delete[] p; cout<<a<<b; // 2 1
}
return 0;
} 85 86

5. Con Trỏ và Hàm Số 5. Con Trỏ và Hàm Số


• Tham số của hàm là 1 biến con trỏ
• Trường hợp không thay đổi giá trị của đối số • Kiểu trả về của hàm là 1 con trỏ
void Capphat(int *a) int* GetArray()
{ a = new int[5]; {
for(int i=0; i<5; i++) int* a = new int [5];
{ a[i]=i+1; for(int i=0; i<5; i++)
cout<< a[i]; a[i]=i+1;
} return a;
} }
void main()
{ int n=5;
int *b = &n;
cout<<*b; // 5
Capphat(b); // 1 2 3 4 5
cout<<*b; // 5
}
87 88

5. Con Trỏ và cấu trúc 5. Con Trỏ và cấu trúc


• Truy xuất các các thuộc tính dùng con trỏ:
tên_biến_con_trỏ  tên_thuộc_tính • Cấu trúc đệ quy (tự trỏ)
• Ví dụ cấu trúc phân số
struct PhanSo struct PERSON
{ int TuSo; {
int MauSo; char hoten[30];
}; struct PERSON *father, *mother;
};
PhanSo x;
x.Tuso=1; x.MauSo=2; struct NODE
PhanSo *p, *q; {
p = &x; int value;
struct NODE *pNext;
PTuso=3; p MauSo=4; // x(3,4)
};

q = new PhanSo();
q->TuSo = 1; // giống (*q).TuSo=1
q->MauSo = 2;// giống (*q).MauSo=2
89

15
9/24/2018

Bài tập

• Hãy tóm tắt lại các kiến thức quan trọng mà em


nghĩ là cần thiết của môn NMLT về các thành phần
sau
 Biến.
 Hàm.
 Mảng.
 Cấu trúc.
 Con trỏ.

92

16

You might also like