Thứ Hai, 5 tháng 3, 2012

Kiểu con trỏ trong ngôn ngữ Pascal


Các biến thuộc kiểu dữ liệu đã học như integer, real, mảng, tập hợp, bản ghi . . . gọi là các biến tĩnh vì chúng được xác định rõ ràng khi khai báo và sau đó được dùng thông qua tên của chúng. Thời gian tồn tại của biến tĩnh cũng là thời gian tồn tại của khối chương trình có chứa khai báo các biến này. Do đó nếu chương trình sử dụng
một số lượng lớn các biến tĩnh thì sẽ không đủ bộ nhớ. Ví dụ khi khai báo var A: array[1..5000] of real;  máy sẽ phân một vùng nhớ cố định 30000 byte cho mảng A, trong chương trình có thể ta không dùng đến cả 5000 phần tử. Để tránh lãng phí bộ nhớ Turbo Pascal cho phép dùng biến động (dynamic variable). Các biến này được lưu trữ trong vùng Heap (vùng nhớ tự do của máy), khi cần chúng có thể được tạo ra để chứa dữ liệu, khi không cần có thể xoá chúng đi để giải phóng bộ nhớ. Biến động không có tên và do một con trỏ quản lý.
1. Khai báo kiểu con trỏ và biến con trỏ
Kiểu con trỏ là một kiểu dữ liệu đặc biệt dùng để lưu trữ những giá trị địa chỉ. Biến con trỏ có độ lớn 4 byte : hai byte thấp chứa địa chỉ offset, 2 byte cao chứa địa chỉ segment. Nhờ biến con trỏ ta có thể xin cấp phát động trên vùng Heap. Có hai kiểu con trỏ: con trỏ định kiểu và con trỏ không định kiểu.
Con trỏ định kiểu là con trỏ cần xác định kiểu dữ liệu mà nó trỏ đến. Cách khai báo kiểu con trỏ và biến con trỏ (theo hai cách):
Type  KiểuConTrỏ = ^KiểuDữLiệu;
Var    BiếnConTrỏ1: KiểuConTrỏ;  BiếnConTrỏ2: ^KiểuDữLiệu;
trong đó KiểuDữLiệu có thể là real, integer, char, mảng, bản ghi… Khi một biến con trỏ chứa địa chỉ byte đầu tiên của một vùng nhớ thì ta nói con trỏ đang trỏ tới vùng nhớ này. Con trỏ real trỏ tới vùng nhớ 6 byte, con trỏ integer trỏ tới vùng nhớ 2 byte…  Kiểu con trỏ cũng có thể là thành phần của bất kỳ một kiểu có cấu trúc nào (mảng, bản ghi). Hai con trỏ định kiểu gọi là tương thích (trong các phép gán, so sánh, truyền dữ liệu cho tham số) nếu cùng trỏ tới một kiểu dữ liệu.
Con trỏ không định kiểu là con trỏ không quan tâm đến kiểu dữ liệu mà nó đang trỏ tới, chỉ quan tâm đến địa chỉ. Cách khai báo:  Var P: Pointer; Con trỏ không định kiểu tương thích với mọi kiểu con trỏ khác.
Ví dụ:
Type   IntPtr = ^Integer;
SVienPtr = ^SVien; { Kiểu bản ghi SVien có thể khai sau }
SVien = Record
           HoTen: string[25];
           DiemTB: real;
        End ;
Var  P1: IntPtr; P: SVienPtr;  P2: ^Word; Q: Pointer;

2. Các thao tác đối với con trỏ
Gán một giá trị địa chỉ cho con trỏ theo các cách: P := @x; P := Addr(x); P := Ptr(segment, offset); trong đó P là biến con trỏ bất kỳ, x là tên biến (hoặc tên hàm, thủ tục). Các hàm Addr, Ptr và toán tử @ trả về một địa chỉ kiểu Pointer.
Hằng NIL. Lệnh gán P := NIL nhằm làm cho con trỏ P không trỏ vào đâu cả (P là con trỏ kiểu bất kỳ).
Phép gán (:=) giữa hai con trỏ: hai con trỏ có thể gán cho nhau trong trường hợp tương thích, khi đó chúng cùng trỏ tới một địa chỉ. Hai con trỏ định kiểu trỏ tới các kiểu dữ liệu khác nhau là không tương thích, song chúng lại tương thích với kiểu Pointer. Ví dụ sau khai báo Var p, s: byte; q : ^real; r: pointer; thì lệnh gán p := s là đúng, lệnh gán q := p; là sai, song hai lệnh sau là đúng: r := p; q := r;
Hai giá trị con trỏ có thể so sánh bằng nhau (=) hay khác nhau (<>) nếu chúng tương thích. Với các giá trị con trỏ không có các phép so sánh  >   >=  <   <=.
Để truy cập nội dung vùng dữ liệu mà con trỏ P đang trỏ ta dùng ký hiệu P^, P^ xem như là một biến thông thường. Nếu P là con trỏ định kiểu thì P^ là biến có kiểu là kiểu dữ liệu mà P trỏ tới. Nếu P là con trỏ không định kiểu thì P^ là một biến không định kiểu có địa chỉ lưu trong P, nó được dùng khi không quan tâm đến kiểu dữ liệu được truy cập.
Chương trình ConTro.pas minh hoạ ý nghĩa của biến con trỏ:
var x: real; p,q: ^real;
begin x:= 123.456789; p:= @x; q:= addr(x);
writeln(‘x = ‘,x:13:5,’  p^ = ‘,p^:13:5,’  q^ = ‘,q^:13:5);
writeln(‘Dia chi bien x : ‘,seg(x),’:’,ofs(x));
writeln(‘Dia chi luu trong con tro p : ‘,seg(p^),’:’,ofs(p^));
readln;
end.
3. Cấp phát động
Đối với biến con trỏ ta có thể dùng kỹ thuật cấp phát động nhờ các thủ tục New, Getmem để tạo các biến động P^ mới. Vùng cấp phát động bao giờ cũng là vùng nhớ tự do (Heap). Turbo Pascal quản lý vùng Heap thông qua con trỏ Heap. Con trỏ Heap luôn luôn trỏ vào byte tự do đầu tiên của vùng nhớ còn tự do của Heap. Mỗi lần dùng thủ tục New tạo ra một biến động thì con trỏ Heap dịch chuyển về phía đỉnh của vùng nhớ tự do một số byte tương ứng với kích thước của biến động mới được tạo.
Các hàm và thủ tục liên quan tới cấp phát động:
* Thủ tục New(P), trong đó P là một con trỏ định kiểu, sẽ cấp phát một vùng nhớ trên Heap có kiểu và kích thước do P quy định, P trỏ tới vùng nhớ vừa cấp (tức là P chứa địa chỉ byte đầu tiên của vùng nhớ), tạo ra biến định kiểu P^ (gọi là biến động) và có thể dùng P^ như một biến thông thường. Vùng nhớ đã cấp phát được hệ thống bảo vệ cho đến khi bị thu hồi. Nếu dùng New(P) nhiều lần thì P chứa địa chỉ vùng nhớ được cấp phát lần cuối cùng.
Thủ tục Dispose(P) thu hồi vùng nhớ đã được cấp phát cho con trỏ P bởi New(P)
Chương trình SVien.pas minh hoạ cách dùng các thủ tục New và Dispose:
type SVienPtr = ^SVien;
SVien = Record
        Hoten: string[25];
        DiemTb: real
        end;
var p,q: SVienPtr;
begin
    new(p);
    p^.hoten:= 'Bui The Tam';
    p^.diemtb:= 5.5;
    q:= p;
    new(p);
    write('Vao ho ten : ')readln(p^.hoten);
    write('Vao diem trung binh : ')readln(p^.diemtb);
    writeln('Sinh vien 1: ',q^.hoten,'  ',q^.diemtb:5:2);
    writeln('Sinh vien 2: ',p^.hoten,'  ',p^.diemtb:5:2);
    dispose(q);
    dispose(p);
    readln;
end.
Chương trình CTMang.pas nêu cách khai báo một biến con trỏ kiểu mảng và cách truy nhập các phần tử của mảng.
const nmax = 1000;
type mang= array[1..nmax] of integer;
var a: ^mang;
    i,n, tong: integer;
begin
    write('Vao n = ); readln(n);
    new(a);
    tong := 0;
    for i:=1 to n do
    begin
        write('
a[',i,'] = '); readln(a^[i]);
        tong:= tong+a^[i]
    end;
    write('
Mang A: ');
    for i:=1 to n do
        write(a^[i],'
 ');
    writeln;
    dispose(a);
    writeln('
Tong = ',tong);
    readln;
end.
Thủ tục GetMem(var P : pointer; n : word) cấp phát n byte trên Heap cho con trỏ P mà không quan tâm đến kiểu dữ liệu, P chứa địa chỉ byte đầu tiên của vùng nhớ, biến động P^ được dùng như một biến không định kiểu. Khối nhớ lớn nhất có thể tạo trên Heap là 65528 byte.
Thủ tục FreeMem(var P : pointer; n : word) thu hồi vùng nhớ n byte được cấp phát bằng GetMem cho con trỏ P.
Chương trình MangDong.pas minh hoạ cách dùng các thủ tục Getmem, Freemem để tạo các mảng động (kích thước của chúng không cần ấn định trước) nhằm tiết kiệm bộ nhớ. Lúc đầu mảng chỉ khai tượng trưng có 1 phần tử, sau đó mảng được cấp phát vùng nhớ vừa đủ cho n phần tử cần dùng.
type mang=array[1..1] of real;
var a:^mang; i,n: integer; tong: real;
begin write(‘n = ‘); readln(n); tong:= 0;
Getmem(a,n* sizeof(real));
for i:=1 to n do
begin write(‘a’,i,’ = ‘); readln(a^[i]); tong:= tong+a^[i] end;
for i:=1 to n do  writeln(‘a[',i,'] = ‘,a^[i]:13:5);
Freemem(a,n* sizeof(real)); writeln(‘Tong = ‘,tong:13:5); readln;
end.
Biến HeapEnd: pointer trỏ tới điểm cuối của Heap được chương trình sử dụng, biến HeapOrg: pointer trỏ tới điểm bắt đầu của Heap được dùng cấp phát động. Các biến HeapEnd và HeapOrg được hệ thống khởi gán khi chương trình bắt đầu chạy và không thay đổi khi chương trình chạy.
Biến HeapPtr: pointer trỏ tới đỉnh của Heap, tức là đỉnh của vùng nhớ cấp phát động và cũng là đáy của vùng nhớ tự do. Khởi đầu giá trị HeapPtr = HeapOrg.
Hàm MemAvail: LongInt cho tổng số byte còn tự do trên Heap. Hàm MaxAvail: LongInt cho số byte liên tục lớn nhất còn tự do trên Heap. Các vùng nhớ còn tự do trên Heap thường bị phân ra thành các khối nhỏ do máy cấp phát và giải toả các vùng nhớ trên Heap không theo một thứ tự nào. Để tránh tràn Heap, trước khi cấp phát bộ nhớ cho một đối tượng có kích thước lớn (như mảng, mảng các bản ghi) ta nên dùng hàm MaxAvail để kiểm tra có đủ bộ nhớ không.
Chương trình Heap.pas minh hoạ cách dùng các biến và hàm ở trên để quản lý Heap. Trong Turbo Pascal cho phép khai báo một mảng có kích thước không quá 64 KB.
type mang1 = array[1..65535] of byte;
mang2 = array[1..100, 1..109] of real;
var a: ^mang1; b: ^mang2; top: pointer; i,j: longint;
begin
    writeln('HeapOrg = ',seg(HeapOrg^),':',ofs(HeapOrg^));
    writeln('HeapEnd = ',seg(HeapEnd^),':',ofs(HeapEnd^));
    writeln('HeapPtr = ',seg(HeapPtr^),':',ofs(HeapPtr^));
    mark(top);
    new(a);
    for i:=to 65535 do a^[i]:=1;
    writeln('MemAvai = ',memavail,'  MaxAvail = ',maxavail);
    if MaxAvail >= sizeof(mang2) then
    begin
        new(b);
        for i:=to 100 do
            for j:=to 109 do
                b^[i,j]:=1.234;
        writeln('MemAvai = ',memavail,'  MaxAvail =' ,maxavail);
    end;
    writeln(a^[65535],'  ',b^[100,109]:8:3);
    release(top);
    writeln('MemAvai = ',memavail,'  MaxAvail = ',maxavail);
    readln;
end.
Thủ tục Mark(var Top: pointer) gán giá trị của con trỏ Heap cho con trỏ Top. Thủ tục này dùng để đánh dấu địa chỉ bắt đầu cấp phát động.
Thủ tục Release(var Top: pointer): thu hồi tất cả các vùng nhớ được cấp phát bắt đầu từ địa chỉ được lưu trong con trỏ Top nhờ thủ tục Mark(Top) cho đến đỉnh Heap hiện tại. Thủ tục Mark và Release  nhằm xoá một loạt các biến động khỏi Heap.
Chương trình MangCT.pas minh hoạ cách dùng các thủ tục Mark, Release và mảng các con trỏ.
const nmax = 1000;
var b: array[1..nmax] of ^integer;
    i,n,tong: integer; top: pointer;
begin
    write('Vao n = ')readln(n);
    tong:=0;
    mark(top);
    for i:=to n do
    begin
        new(b[i]);
        write('b[',i,'] = ')readln(b[i]^);
        tong:= tong+b[i]^;
    end;
    for i:=to n do
        write(b[i]^,'   ');
    writeln;
    release(top);
    writeln('Tong = ',tong);
    readln;
end.
Share this post
  • Share to Facebook
  • Share to Twitter
  • Share to Google+
  • Share to Stumble Upon
  • Share to Evernote
  • Share to Blogger
  • Share to Email
  • Share to Yahoo Messenger
  • More...

0 nhận xét

:) :-) :)) =)) :( :-( :(( :d :-d @-) :p :o :>) (o) [-( :-? (p) :-s (m) 8-) :-t :-b b-( :-# =p~ :-$ (b) (f) x-) (k) (h) (c) cheer

 
© Download do an khoa luan tai lieu
Designed by BlogThietKe Cooperated with Duy Pham
Released under Creative Commons 3.0 CC BY-NC 3.0
Posts RSSComments RSS
Back to top