Bài giảng Lập trình hướng đối tượng - Nguyễn Thị Điệu - Chương 8: Con trỏ
8.1. Địa chỉ vàcon trỏ
8.2. Con trỏ, mảng vàxâu ký tự
8.3. Quản lý bộ nhớ vớinew vàdelete
8.4. Bài tập chương8
Tóm tắt nội dung Bài giảng Lập trình hướng đối tượng - Nguyễn Thị Điệu - Chương 8: Con trỏ, để xem tài liệu hoàn chỉnh bạn click vào nút "TẢI VỀ" ở trên
;<"Ky tu nam trong bien ch la:
"<<**mp;
8.2. Con trӓ, mảng và xâu ký tӵ
19
8.2.1. Con trỏ và mảng
8.2.2. Con trỏ và xâu ký tự
8.2.1. Con trӓ và mảng
20
Con trỏ được sử dụng để truy nhập vào các phần tử của
mảng và làm đối số truyền vào hàm. Và khi mảng làm đối số
truyền vào hàm thì con trỏ cũng rất hữu ích.
Các phần tử của mảng có thể được truy nhập qua ký hiệu của
mảng ([]) hoặc ký hiệu của con trỏ (*). Ví dụ:
int a[5]={31,54,77,52,93};
int i;
//Dua ra bang ky hieu cua mang
for(i=0;i<5;i++) cout<<a[i]<<'\n';
//Dua ra bang ky hieu cua con tro
for(i=0;i<5;i++) cout<<*(a+i)<<'\n';
8.2.1. Con trӓ và mảng (tiếp)
21
Biểu thức *(a+i) tương đương với a[i]. Ví dụ, với i=2
thì *(a+2) là phần tử thứ 3 (có giá trị là 77).
Tại sao *(a+2) lại là phần tử thứ 3? Như ta đã biết, tên
biến mảng chính là địa chỉ của phần tử đầu tiên của
biến mảng. Khi ta viết (a+2) thì trình biên dịch sẽ thực
hiện cộng địa chỉ với 2. Khi cộng địa chỉ với 2 trình
biên dịch lấy kích thước kiểu dữ liệu của mảng
nhân với 2 rồi mới cộng vào địa chỉ. Kết quả (a+2)
cho ta địa chỉ của phần tử thứ 3. Để truy nhập tới phần
tử thứ 3 khi biết địa chỉ phải sử dụng toán tử truy nhập
gián tiếp *(a+2).
8.2.1. Con trӓ và mảng (tiếp)
22
Địa chỉ của các phần tử mảng: int a[5]={31,54,77,52,93};
a[3]
a[2]
a[1]
a[0]
a[4]
a+3
a+2
a+1
a
a+4
Địa chỉ của
các phần tử
93
52
77
54
31
8.2.1. Con trӓ và mảng (tiếp)
23
Hằng con trӓ và biến con trӓ: Tên biến mảng là một
địa chỉ cụ thể mà hệ thống đã chọn để đặt mảng. Địa chỉ
này không thể thay đổi và nó được duy trì khi biến
mảng còn tồn tại. Ngưӡi ta gọi các địa chỉ không thay đổi
được là các hằng con trỏ. Vì tên biến mảng a ӣ ví dụ trên là
hằng nên ta không thể viết a++ hay a+=2.
Một địa chỉ thì không thể thay đổi nhưng biến con trỏ
chứa địa chỉ thì có thể thay đổi.
8.2.1. Con trӓ và mảng (tiếp)
24
Hằng con trӓ và biến con trӓ: (tiếp).
Ví dụ sau dùng biến con trỏ để đưa ra các phần tử của
mảng:
int a[5]={31,54,77,52,93};
int i;
int *p=a; //p tro toi phan tu dau tien cua mang a
//Dua ra bang bien con tro
cout<<"Dua ra bang bien con tro: "<<'\n';
for(i=0;i<5;i++) cout<<*(p++)<<' ';
8.2.2. Con trӓ và xâu ký tӵ
25
Như ta đã biết, xâu ký tự thực chất là mảng ký tự. Bӣi vậy ta có
thể dùng ký hiệu con trỏ để truy nhập vào các ký tự của xâu
giống như truy nhập vào các phần tử của mảng. Ví dụ:
char s[5]=”DHNNI”;
cout<<*(s+1);//Dua ra ky tu thu 2 la H
Con trӓ trӓ tӟi hằng xâu ký tӵ: Khi khai báo và khӣi tạo biến
xâu ký tự ta có thể khai báo như một mảng ký tự hoặc khai báo
như một con trỏ trỏ tới kiểu ký tự. Ví dụ:
char s1[] = ”Khai bao nhu mot mang”;
char* s2 = ”Khai bao nhu con con tro”;
8.2.2. Con trӓ và xâu ký tӵ (tiếp)
26
Sau khai báo trên ta sẽ được hai biến xâu ký tự s1 và s2. Tuy
nhiên hai biến xâu này có một sự khác nhau: s1 là một địa chỉ,
một hằng con trỏ, s2 là một biến con trỏ; s2 có thể thay đổi còn
s1 không thể thay đổi. Ví dụ:
Chú ý: Khi thay đổi s2 thì ký tự đầu tiên của xâu sẽ thay đổi. Ӣ
ví dụ trên, sau khi tĕng s2 lên 1 thì ký tự đầu tiên của xâu là h.
char s1[]="Khai bao nhu mot mang";
char* s2="Khai bao nhu mot con tro";
cout<<s1<<'\n';
cout<<s2<<'\n';
//s1++; //Bao loi, s1 la hang con tro
s2++; //Duoc
cout<<s2; //Chi hien: hai bao nhu mot con tro
8.2.2. Con trӓ và xâu ký tӵ (tiếp)
27
Mảng con trӓ trӓ tӟi các hằng xâu ký tӵ:
Giống như mảng các biến kiểu int hoặc float, ta cũng có
mảng con trỏ. Mảng con trỏ hay dùng nhất là mảng con
trỏ trỏ tới các hằng xâu ký tự. Ta xét hai cách khai báo sau đây:
//Dùng mảng hai chiều
char days[7][10]={"Sunday","Monday","Tuesday","Wednesday",
"Thursday","Friday","Saturday"};
//Dùng con trỏ
char* days[7]={"Sunday","Monday","Tuesday","Wednesday",
"Thursday","Friday","Saturday"};
8.2.2. Con trӓ và xâu ký tӵ (tiếp)
28
Mảng con trӓ trӓ tӟi các hằng xâu ký tӵ: (tiếp)
Nếu khai báo theo mảng hai chiều thì các mảng con chứa các
xâu ký tự phải có kích thước bằng nhau (10). Do đó, với
những xâu có số ký tự nhỏ hơn 10 sẽ gây lãng phí bộ nhớ.
Nếu khai báo theo con trỏ thì trình biên dịch C++ sẽ để các
xâu ký tự liên tiếp nhau trong bộ nhớ và dùng một mảng con
trỏ để trỏ tới các xâu này. (Hình trang sau cho thấy các xâu
ký tự trong bộ nhớ). Một xâu ký tự là một mảng kiểu char,
do đó một mảng con trỏ trỏ tới xâu ký tự thực chất là một
mảng con trỏ trỏ tới char. Đây chính là lý do tại sao ta khai
báo là char*
8.2.2. Con trӓ và xâu ký tӵ (tiếp)
29
Địa chỉ của ký tự
đầu tiên chính là
địa chỉ của xâu.
Các địa chỉ này
được lưu trữ trong
mảng con trỏ.
Các xâu ký tự
f200 S
f199 u
f198 n
f197 d
f196 a
f195 y
f194 \n
f193 M
f192 o
f191 n
f190 d
f189 a
y f188
f187 \n
f186 T
u f185
e f184
Mảng con trỏ
f200
f193
f186
f178
f168
f168
f160
f153
f144
8.3. Quản lý bӝ nhӟ vӟi new và delete
30
8.3.1. Cách sử dụng bộ nhớ của một chương trình C++
8.3.2. Hạn chế của mảng
8.3.3. Toán tử new và delete
8.3.4. Khӣi tạo ô nhớ được cấp phát động
8.3.5. Mảng động
8.3.1. Cách sử dụng bӝ nhӟ của mӝt chѭơng
trình C++
31
Một chương trình C++ khi
chạy sẽ chiếm một vùng nhớ
trong bộ nhớ. Vùng nhớ này
được chia thành 3 phần: phần
chứa mã chương trình, phần
chứa các biến tĩnh và biến
ngoài (gọi là Heap), phần
chứa các biến tự động (gọi
là Stack). Stack mӣ rộng từ
địa chỉ cao xuống địa chỉ
thấp, Heap mӣ rộng từ địa chỉ
thấp lên địa chỉ cao.
Stack
Heap
Biến toàn cục
Mã chương
trình
Địa chỉ cao
Địa chỉ thấp
8.3.2. Hạn chế của việc lѭu trӳ bằng mảng
32
Mảng rất hay được sử dụng khi cần lưu trữ một số lượng lớn các biến
hay đối tượng. Tuy nhiên tại thӡi điểm viết chương trình ta phải xác
định kích thước của mảng chứ không đợi được đến khi chương trình
thực hiện. Đoạn chương trình sau sẽ sinh ra lỗi:
int size;
cin>>size; //Lấy kích thước mảng
int a[size]; //Lỗi, kích thước mảng phải là hằng
Trong nhiều trưӡng hợp, tại thӡi điểm viết chương trình ta không
biết được là cần bao nhiêu bộ nhớ. Nếu dự trù nhiều mà không dùng
hết thì lãng phí bộ nhớ, nếu dự trù ít mà cần lưu trữ nhiều thì không
có chỗ chứa. Vấn đề này được khắc phục bằng cơ chế cấp phát động
bộ nhớ nhӡ toán tử new và delete.
8.2.3. Toán tử new và delete
33
C++ có 2 toán tử thực hiện chức nĕng cấp phát và giải
phóng bộ nhớ. Cú pháp như sau:
Trong đó Biến_con_trỏ phải được khai báo trỏ đến
kiểu dữ liệu của biến.
Toán tử new sẽ cấp phát một ô nhớ trong phần nhớ
Heap, trong khi chương trình đang chạy, đủ để chứa
một giá trị có kiểu Kiểu_dl_của_biến và trả về một
con trỏ trỏ tới nó.
Biến_con_trỏ = new Kiểu_dl_của_biến; //Cấp phát
delete Biến_con_trỏ; //Giải phóng bộ nhớ
8.2.3. Toán tử new và delete (tiếp)
34
Toán tử delete sẽ giải phóng vùng nhớ được trỏ tới
bӣi biến con trỏ. Chỉ nên dùng delete để giải phóng
vùng nhớ được cấp phát bӣi new. Nếu dùng delete
để giải phóng các vùng nhớ không được cấp phát bӣi
new sẽ gây ra nhiều nguy hiểm.
Vì kích thước phần Heap có giới hạn nên có thể sẽ hết.
Nếu phần nhớ Heap đã hết mà ta vẫn cấp phát thì new
sẽ trả về con trỏ rỗng. Bӣi vậy, luôn luôn phải kiểm
tra con trỏ được trả về bӣi new trước khi dùng nó.
8.2.3. Toán tử new và delete (tiếp) ậ Ví dụ:
35
//Khai bao su dung thu vien chuong trinh
#include
#include
int main()
{clrscr();
int* p;
p=new int; //cap phat bo nho chua kieu int
if(!p)
{
cout<<"Cap phat bo nho bi loi";
return 1;
}
*p=100; //Gan 100 vao o nho vua duoc cap
cout<<*p; //Hien thi noi dung cua o nho vua duoc cap
delete p; //Giai phong o nho vua duoc cap
getch(); //Dung chuong trinh lai de xem ket qua
return 0;
}
8.2.4. Khởi tạo ô nhӟ đѭӧc cấp phát đӝng
36
Ta có thể khӣi tạo giá trị cho các ô nhớ được cấp phát
động bӣi new. Giá trị khӣi tạo phải đặt trong ngoặc
đơn sau tên kiểu dữ liệu.
Ví dụ:
int* p;
p = new int(1200);
cout<<*p;
8.2.5. Mảng đӝng
37
Với cơ chế cấp phát động bộ nhớ ta có thể cấp phát bộ nhớ cho
cả một biến mảng. Điều này cho phép xác định số phần tử của
mảng trong khi chạy chương trình. Cú pháp cấp phát động cho
mảng một chiều như sau:
Biến_con_trӓ = new Kiểu_của_mảng[size];
trong đó size là số phần tử của mảng, size có thể là hằng, biến
hoặc biểu thức.
Ví dụ: int size; cin>>size;
float* p=new float[size]; Để giải phóng vùng nhớ cấp phát cho mảng ta dùng toán tử
delete: delete [] Biến_con_trӓ;
Ví dụ: delete [] p;
8.2.5. Mảng đӝng (tiếp) ậ Ví dụ:
38
//Khai bao su dung thu vien chuong trinh
#include
#include
int main()
{
clrscr();
int* p,*q;
int n,i;
cout>n;
p=new int[n]; //Cap phat bo nho cho mang n phan tu nguyen
if(!p)
{
cout<<"Cap phat bo nho bi loi";
return 1;
}
//Tiếp trang sau
8.2.5. Mảng đӝng ậ Ví dụ (tiếp):
39
//Nhap cac gia tri vao mang
cout<<"Nhap vao mang so nguyen:\n";
q=p;
for(i=0;i<n;++i)
{
cout>*q++;
}
//Dua cac so nhap vao ra man hinh
cout<<"Cac so da nhap la:\n";
q=p;
for(i=0;i<n;++i) cout<<*q++<<' ';
delete [] p; //Giai phong vung nho cap phat cho mang
getch(); //Dung chuong trinh lai de xem ket qua
return 0;
}
BÀI TҰP VÀ THӴC HÀNH
40
Bài 1. Viết chương trình nhập vào một dãy n số nguyên,
lưu dãy số này trong một danh sách liên kết đơn P.
Hãy tạo một danh sách liên kết đơn Q là đảo ngược của
P.
Bài 2. Viết chương trình nhập vào một dãy n số nguyên,
lưu dãy số này trong một danh sách liên kết đơn P.
Hãy sắp xếp dãy số theo chiều không giảm sử dụng
phương pháp sắp xếp chọn.
File đính kèm:
Bài giảng Lập trình hướng đối tượng - Nguyễn Thị Điệu - Chương 8 Con trỏ.pdf

