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

pdf40 trang | Chuyên mục: Lập Trình Hướng Đối Tượng | Chia sẻ: dkS00TYs | Lượt xem: 1607 | Lượt tải: 2download
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:

  • pdfBài giảng Lập trình hướng đối tượng - Nguyễn Thị Điệu - Chương 8 Con trỏ.pdf
Tài liệu liên quan