Ứng dụng danh sách liên kết cài đặt: STACK và QUEUE Chương 2 : Quản lý bộ nhớ, kiểu dữ liệu con trỏ và biến con trỏ trong Pascal Mục đích: Tìm hiểu về cấp phát bộ nhớ cho các đối tượng
Trang 1LẬP TRÌNH PASCAL NÂNG CAO
NỘI DUNG CHÍNH :
Chương 1 Chương trình con - Unit
Chương 2 Kiểu dữ liệu con trỏ và biến con trỏ
Chương 3 Danh sách liên kết
Chương 4 Ứng dụng danh sách liên kết cài đặt: STACK và QUEUE
Chương 2 : Quản lý bộ nhớ, kiểu dữ liệu con trỏ và biến con trỏ trong
Pascal
Mục đích:
Tìm hiểu về cấp phát bộ nhớ cho các đối tượng trong chương trình
Khai thác kiểu dữ liệu địa địa (con trỏ)
Nội dung :
Cấu trúc chương trình sau khi dịch và cấp phát bộ nhớ tương ứng
Kiểu dữ liệu địa chỉ (kiểu dữ liệu con trỏ)
Biến lưu trữ địa chỉ (biến động, biến con trỏ)
I Bộ nhớ và cách quản lý địa chỉ :
Bus địa chỉ: 16 bit
Hạn chế không gian bộ nhớ có thể quản lý được: 216= 65536 ô nhớ (byte) được quản lý
Giải pháp:
Chia không gian bộ nhớ thành nhiều đoạn (segment), mỗi đoạn có 65536 ô nhớ (64KB) Mỗi đoạn được đánh địa chỉ lại từ đầu (offset)
Như vậy, địa chỉ được xác định thông qua hai thành phần: segment:offset Cấu trúc CT trong bộ nhớ
Program segment prefix: 256 byte dùng để lưu địa chỉ cơ sở khi chương trình được tải vào
Pascal: PrefixSeg Code segment: đoạn chứa mã CT chính và các đoạn chứa mã của Unit liên kết, CT con,
Pascal: Cseg Data Segment: lưu các biến, kiểu dl, hằng toàn cục
Pascal: Dseg Stack segment: vùng cấp phát cho đối tượng cục bộ khi CTC được gọi đến
Chú ý: Đỉnh Stack được phát trỉên từ địa chỉ cao đến địa chỉ thấp
Pascal: SSeg xác định đoạn Stack của CT;
SPtr xác định đỉnh Stack
Heap: Vùng tự do trên cùng, dành cho dữ liệu cấp phát động
Data segment Đoạn dữ liệu Program segment prefix
Code segment
Đoạn mã Stack Vùng ngăn xếp Heap Vùng tự do
II Kiểu dữ liệu địa chỉ (con trỏ) và biến con trỏ :
1 Kiểu DL con trỏ:
Là kiểu DL đặc biệt biễu diễn địa chỉ của các đối tượng (CTC, biến, )
Có hai loại kiểu dữ liệu địa chỉ trong Pascal:
Định kiểu: địa chỉ của những đối tượng có xác định định kiểu DL Không định kiểu: địa chỉ của những đối tượng không xác định kiểu DL Mỗi đối tượng được xác định qua: 1 word địa chỉ tương đối (offset) và 1 word địa chỉ đoạn (segment) Tương đương 4 byte
Trang 2a/ Kiểu dữ liệu con trỏ định kiểu :
Là kiểu dữ liệu biểu diễn địa chỉ của các đối tượng có xác định kiểu: ví dụ: địa chỉ của kiểu dữ liệu
số nguyên, địa chỉ của kiểu dữ liệu bản ghi thisinh,
Định nghĩa: Dùng dấu ^ trước tên kiểu dữ liệu
Ví dụ: Type
Pint =^Integer;
Preal=^real;
Pthisinh=^thisinh; { kiểu dữ liệu thisinh có thể đã được định nghĩa trước đó hoặc sau định nghĩa này}
Chú ý: Kiểu dữ liệu địa chỉ định kiểu bắt buộc phải được định nghĩa qua tên kiểu dữ liệu đối tượng
Ví dụ: các định nghĩa sau là sai:
Type
Pstr20 =^string[20];
Pmang=^array[1 10] of real;
Giải pháp:
Type
str20=string[20];
Pstr20=^str20;
b/ Kiểu dữ liệu địa chỉ không định kiểu đối tượng :
là kiểu địa chỉ biểu diễn chung cho địa chỉ của các đối tượng, không cần xác định kiểu dữ liệu đối tượng
Xác định bằng từ khoá: Pointer
Biến được khai báo với kiểu dữ liệu địa chỉ Pointer sẽ tương thích với các kiểu địa chỉ khác
2 Biến con trỏ :
Biến tĩnh (biến khai báo có kiểu dữ liệu đối tượng) được cấp phát bộ nhớ tương ứng với kích thước của kiểu tại thời điểm biên dịch chương trình
Tại thời điểm lập trình, người lập trình không thể biết trước số lượng phần tử của một mảng Biến con trỏ là biến được khai báo với kiểu dữ liệu con trỏ Đó là các biến mà giá trị của chúng là địa chỉ ô nhớ
Biến con trỏ được cấp phát trên vùng Heap của chương trình trong quá trình CT được thi hành Khai báo biến con trỏ:
Biến con trỏ được khai báo giống như biến tĩnh bình thường
Var biến_trỏ: kiểu_DL_địa chỉ;
Có thể khai báo qua kiểu dữ liệu địa chỉ đã định nghĩa hoặc khai báo biến con trỏ trực tiếp
Ví dụ khai báo biến con trỏ:
Var
p: pint; {khai báo thông qua định nghĩa}
q:^real;{khai báo trực tiếp}
L:^thisinh;
T:pthisinh;
f:pointer;
3 Làm việc với biến con trỏ
a/ phép gán giá trị địa chỉ cho biến con trỏ :
Một giá trị địa chỉ có thể được gán cho biến con trỏ bằng các phương pháp sau:
Toán tử @ :
p:=@x;{đưa địa chỉ của x vào p}
p là một biến con trỏ có kiểu hợp lý hoăc kiểu Pointer, x là một biến, CTC
Hàm addr :
p:=addr(x);
Cả hai cách trên ta đều nói p trỏ vào x
Hàm Ptr :
p:=Ptr(seg,off);
Trang 3seg và off là các biến kiểu word xác định địa chỉ đoạn và địa chỉ tương đối.
p trỏ vào ô nhớ có địa chỉ seg:off b/ Giá trị Nil :
Khi khai báo một biến con trỏ, biến này có thể trỏ đến một ví trí bộ nhớ bất kỳ nào đó
Sẽ rất nguy hiểm nếu chúng ta vô tình thay đổi giá trị tại ô nhớ mà con trỏ đó đang trỏ đến
Giá trị Nil được thiết kế để gán cho biến con trỏ nhằm chỉ ra rằng biến con trỏ chưa trỏ vào một địa chỉ nào cả
ví dụ : p:=nil; q:=nil;
c/ Phép gán hai con trỏ :
Hai biến con trỏ có thể gán cho nhau nếu chúng tương thích được với nhau Gán hai con trỏ tức là đưa hai con trỏ có cùng giá trị địa chỉ hay nói cách khác là cùng trỏ vào một địa chỉ
Chú ý rằng hai con trỏ định kiểu khác nhau thì không tương thích nhưng lại tương thích với con trỏ không định kiểu
Ví dụ:
Var
p,q:^integer;
r:^real;l:pointer;
p:=q;
{đưa con trỏ p trỏ vào ô nhớ mà q đang trỏ }
p:=r; {không tương thích}
l:=r; p:=l; {OK}
d/ Phép so sánh hai con trỏ :
Có hai phép toán so sánh giá trị địa chỉ tương thích (so sánh con trỏ)
so sánh =
kiểm tra xem hai con trỏ có cùng trỏ vào một địa chỉ hay không
so sánh <>
kiểm tra xem hai con trỏ có trỏ vào hai địa chỉ khác nhau không e/ Truy cập dữ liệu tại vị trí con trỏ trỏ đến :
Thực tế đích cuối cùng là làm việc với dữ liệu chứ không phải làm việc với địa chỉ
để truy cập đến giá trị nội dung của biến (ô nhớ) mà con trỏ p đang trỏ tới thì ta viết: p^
ví dụ:
gán giá trị ô nhớ có địa chỉ được con trỏ p trỏ tới là 100 thì ta viết:
p^:=100;
in giá trị dữ liệu tại ô nhớ do p trỏ đến:
writeln(p^);
Chú ý:
giả sử p là con trỏ định kiểu T, thì p^ tương đương với biến có kiểu dữ liệu T
khi thay đổi giá trị p thì p^ có giá trị thay đổi theo, đó là giá trị dữ liệu của biến được p trỏ tới
Ví dụ: ???
4 Cấp phát bộ nhớ động :
Trong thân chương trình, khi cần tạo ra một vùng nhớ tương ứng với một đối tượng nào đó, người lập trình
có thể thực hiện Thao tác này được gọi là cấp phát bộ nhớ động
Bộ nhớ động được cấp phát trên vùng Heap
Ví dụ : Tạo danh sách thí sinh??
a/ Cấp phát bộ nhớ cho con trỏ định kiểu :
Giả sử p là con trỏ định kiểu
Câu lệnh new(p) sẽ tạo ra trên vùng Heap một vùng nhớ với kích thước bằng kích thước của kiểu
dữ liệu tương ứng và gán địa chỉ vùng đó cho con trỏ p Giá trị dữ liệu tại địa chỉ đó sẽ được xác định qua p^
Ví dụ : ???
Ứng dụng Cấp phát bộ nhớ cho một mảng động trên vùng Heap
VD : type M=array[1 100,1 100] of real;
Trang 4var a,b:M; tạo ra 10000 x2 x 6 byte trong đoạn dữ liệu?????
Giải pháp:
var a,b:^M;
Ví dụ biến được tạo trên vùng heap???
b/ Thu hồi bộ nhớ do con trỏ định kiểu trỏ đến :
Dispose(p);
c/ Cấp phát bộ nhớ cho con trỏ không định kiểu :
Sử dụng thủ tục getmem(var p:pointer; size:word);
vd: getmem(p,sizeof(n));
d/ Thu hồi một đoạn bộ nhớ :
Sử dụng thủ tục:
freemem(var p:pointer; size:word);
Đánh dấu đoạn:
Mark( var Top:pointer);
release(var Top:pointer);
5 Con trỏ kiểu mảng và mảng con trỏ :
Con trỏ kiểu mảng tức là tạo ra con trỏ mà kiểu dữ liệu nó tham chiếu tới là một mảng
Vd: type M=array[1 9] of real;
var a: ^M;
Mảng các con trỏ tức là tạo ra một mảng mà mỗi phần tử là một con trỏ
VD: var a:array[1 9] of ^real;
6 Một số ví dụ :
- Viết chương trình tính s=1+2+ +n bằng cách sử dụng tất cả các biến con trỏ
- Viết chương trình s=1-2+3+ +(-1)n+1n
- Sử dụng mảng con trỏ hãy viết chương trình nhập và sắp xếp tăng dần mảng các số nguyên -xây dựng chương trình quản lý thisinh bằng cách tạo ra mảng các con trỏ mỗi con trỏ là một thí sinh
- xây dựng chương trình quản lý thí sinh bằng cách tạo một con trỏ mảng