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