Cấp phát động

Một phần của tài liệu Ngôn ngữ lập trình Pascal ĐH Hoa Lư (Trang 119)

1. Con trỏ và biến động

1.5. Cấp phát động

a, Quản lý vùng nhớ Heap

Cấp phát bộ nhớ động là việc cấp phát bộ nhớ được thực hiện bởi câu lệnh trong thân chương trình chứ không phải bằng cách khai báo biến hoặc tham số. Khi một biến được cấp phát bộ nhớ động thì nó trở thành biến động (Dynamic Variable). Vùng nhớ dành cho cấp phát động bao giờ cũng là vùng nhớ tự do Heap. Theo mặc định khi một biến động được hình thành thì địa chỉ của nó được lưu trong biến con trỏ tương ứng.

b, Thủ tục cấp phát bộ nhớ cho con trỏ định kiểu

Với một con trỏ định kiểu p thủ tục cấp phát bộ nhớ động sẽ là: New(p); Thủ tục New(p) cùng một lúc thực hiện các công việc sau:

* Cấp phát một vùng nhớ trên Heap với kích thước bằng kích thước kiểu dữ liệu mà con trỏ trỏ tới.

* Tạo một biến động định kiểu p^ để có thể truy nhập vào vùng dữ liệu của đối tượng. * Lưu địa chỉ của đối tượng vào con trỏ p

Khi một biến động p^ đã hết giá trị sử dụng thì cần thu hồi vùng nhớ đã cấp phát cho nó để dùng vào việc khác. Thủ tục thu hồi là

Dispose(p);

Sau khi thu hồi vùng nhớ mặc dù biến p^ vẫn còn tồn tại nhưng dữ liệu trong vùng nhớ đã dành cho p sẽ không được bảo vệ, điều này có nghĩa là hệ thống có thể dùng vùng nhớ này vào việc khác.

c, Thủ tục cấp phát bộ nhớ cho con trỏ không định kiểu

Pascal có hai thủ tục cấp phát và thu hồi vùng nhớ cho con trỏ không định kiểu p là: Getmem(p, pn); cấp cho p n Byte.

Freemem(p, n); thu hồi n Byte vùng nhớ đã cấp cho p.

Việc cấp phát không định kiểu thường được dùng trong trường hợp chưa biết trước kích thước của đôí tượng.

2.Danh sách liên kết 2.1. Định nghĩa

Với con trỏ kiểu mảng thì việc lập trình tạo danh sách cũng như duyệt danh sách khá đơn giản, hạn chế của kiểu mảng là phải khai báo trước kích thước mảng do đó nhiều khi dẫn tới không tiết kiệm bộ nhớ. Sử dụng biến động chúng ta có thể khắc phục được nhược điểm này bằng cách cần đến đâu thì tạo biến động đến đó. Số biến động tạo ra chỉ bị hạn chế bởi bộ nhớ trong (Ram) của máy PC mà cụ thể là phụ thuộc vào kích thước vùng Heap. Danh sách được hiểu là một tập hợp hữu hạn các phần tử liên kết với nhau, trường hợp tổng quát nhất mỗi phần tử là một bản ghi. điều đặc biệt của mỗi bản ghi trong danh sách là ngoài các trường dữ liệu, còn một trường dùng để liên kết và trường này lại là một con trỏ. Con trỏ này có nhiệm vụ trỏ vào địa chỉ của bản ghi kế tiếp. Nếu bản ghi hiện thời là bản ghi cuối cùng thì con trỏ sẽ trỏ vào Nil.

Chú ý: - Một danh sách chưa có phần tử nào đươc gọi là danh sách rỗng.

- Việc thêm một phần tử vào danh sách có thể rơi vào một trong ba khả năng: * Phần tử mới được thêm vào đầu danh sách

* Phần tử mới được nối vào cuối danh sách * Phần tử mới được chèn vào một vị trí xác định.

Trường hợp 1 chúng ta có danh sách liên kết ngược (LIFO), còn trường hợp 2 chúng ta có danh sách liên kết thuận (FIFO) hay còn gọi là hàng đợi QUEUE.

2.2. Danh sách liên kết ngược

Là loại danh sách mà trường liên kết của phần tử tạo ra sau luôn trỏ vào phần tử tạo ra trước đó. Trường liên kết của phần tử tạo ra đầu tiên trỏ vào Nil. điều này dẫn tới việc khi kết xuất thông tin ra chúng ta phải bắt đầu từ phần tử tạo ra cuối cùng vì chỉ có như vậy chúng ta mới biết địa chỉ của phần tử tạo ra trước đó. Nếu cố tình đi từ phần tử tạo ra dầu tiên thì không thể biết phần tử tiếp theo là phần tử nào. Liên kết kiểu này gọi là kiểu LIFO (Last In- Firt Out) hay còn gọi là kiểu xếp chồng. Phần tử nhập vào cuối cùng sẽ được lấy ra đầu tiên. Danh sách tạo ra theo kiểu này được gọi là danh sách liên kết ngược.

Ví dụ: Giả thiết rằng chúng ta cần xây dựng một danh sách sinh viên với các trường dữ liệu là Mhs (mã hồ sơ), Hoten (Họ và tên), Diem (điểm tổng kết) , trường liên kết lấy tên là next (tiếp tục). Kiểu dữ liệu bản ghi với các trường nêu trên lấy tên là sinhvien. để tạo ra danh sách sinh viên chúng ta cần tạo ra một kiểu con trỏ DS trỏ vào kiểu dữ liệu sinhvien và trường liên kết next trong bản ghi sinhvien sẽ trỏ vào kiểu dữ liệu Ds.

Ds = ^sinhvien; sinhvien= Record Mhs:byte;

Hoten: string[20]; Diem:real; Next: Ds;

End;

Sau khi khai báo kiểu dữ liệu cần khai báo biến con trỏ Ds để lưu trữ dữ liệu nhập vào và biến Ctcuoi (con trỏ cuối) để trỏ vào phần tử cuối cùng. Vấn đề là làm thế nào để con trỏ liên kết next luôn trỏ vào phần tử tạo ra trước đó. Để làm việc này chúng ta tạo ra biến động Ds để lưu trữ dữ liệu. Cứ mỗi bản ghi cần nhập vào thì tạo ra một biến động Ds mới. địa chỉ của biến động Ds luôn được gán cho con trỏ cuối Ctcuoi.

2.3. Hàng đợi Queue - Danh sách liên kết thuận

Với loại danh sách mà phần tử nào nhập trước thì được lấy ra trước chúng ta gọi là danh sách liên kết thuận, nó cũng giống như hàng đợi người nào đến trước thì được gọi trước. Để tạo ra hàng đợi ngoài ctcuoi chúng ta phải thêm vào con trỏ đầu (ctdau). Con trỏ cuối ctcuoi luôn trỏ vào phần tử cuối cùng, còn con trỏ đầu lại luôn trỏ vào phần tử đầu của danh sách.

Program danh_sach_lien_ket_thuan; Uses crt; Type ds= ^sinhvien; sinhvien=record Mhs: Byte; Hoten:string[25]; Diem:real; next:ds; End; Var

dsl, ctdau, ctcuoi:ds; bd:pointer; lam:char; i,j:byte;

Procedure Hien_FIFO; {Hien du lieu tu dau xuong cuoi} Var ct1:ds; Begin

Clrscr;

writeln('Du lieu da nhap - Hien tu dau xuong cuoi'); writeln;

ct1:=ctdau;

while ct1<>nil do with ct1^ do Begin Write(Mhs,' ',hoten);

for j:=1 to (20-length(hoten)) do write(' '); writeln(diem:5:2);

End;

Begin {Than chuong trinh chinh} clrscr;

mark(bd); ctdau:=nil; i:=0; Repeat New(dsl); {tạo biến động Dsl} i:=i+1;

With dsl^ do Begin

Witeln('Ma ho so so: ',i); mhs:=i;

Write('Ho va ten: '); readln(hoten); {nhập dữ liệu} Write('Diem : '); Readln(diem);

if ctdau=nil then

ctdau:=dsl {ctdau trỏ vào phần tử thứ 1 } else

ctcuoi^.next:=dsl; {Lưu trữ địa chỉ của phần tử hiện thời - kể từ phần tử thứ hai vào trường liên kết Next của Ctcuoi }

ctcuoi:=dsl; {ghi nhận lại con trỏ cuối, nghĩa là Next đang trỏ vào phần tử hiện thời}

ctcuoi^.next:=nil; { trường liên kết Next của con trỏ cuối trỏ vào Nil - ket thuc danh sach} Writeln('Nhap next hay thoi? C/K '); lam:=readkey; writeln; End;

Until lam in ['k','K']; Hien_FiFO; Release(bd); End.

2.4. Các thao tác trên danh sách

a, Chèn thêm phần tử vào danh sách

Việc đầu tiên khi muốn chèn thêm phần tử vào danh sách là phải xác định được chính xác vị trí cần chèn, muốn vậy danh sách phải có một trường khoá, mỗi phần tử trong danh sách sẽ ứng với một và chỉ một giá trị của trường khoá. Ví dụ có thể lấy trường số thứ tự (STT) hoặc mã hồ sơ (MHS) làm trường khoá, không thể dùng trường Hoten để làm khoá vì trong danh sách có thể có nhiều người trùng Hoten.

Quá trình chèn một phần tử vào danh sách sẽ qua các bước: * Xác định vị trí chèn

* Tạo một biến động (xem như một con trỏ nháp) và xin cấp phát vùng nhớ cho biến

động để lưu dữ liệu sẽ chèn vào danh sách.

* Chuyển trường Next của phần tử hiện thời đến phần tử bổ xung

* Chuyển trường Next của phần tử bổ xung đến phần tử trước phần tử hiện thời. New(ct1);

With ct1^ do Nhập dữ liệu cho phần phần tử bổ xung dsl:=ctcuoi;

While (dsl<>nil) and (dsl^.mhs <> n) do dsl:=dsl^.next;

ct1^.next:=dsl^.next dsl^.next:=ct1;

b, Xoá một phần tử khỏi danh sách

Quá trình xoá một phần tử trong danh sách không phải là quá trình làm rỗng ô nhớ chứa phần tử mà đơn giản chỉ là chuyển hướng trường liên kết không trỏ vào phần tử đó nữa. Nếu chúng ta xoá nhiều phần tử thì bộ nhớ sẽ bị phân mảnh ra nhiều đoạn ngắt quãng, trong trường hợp này cần bố trí lại các ô nhớ để một đối tượng được bố trí trên một số ô nhớ liên tục

3. Tạo thư viện các chương trình con (UNIT)3.1. Khái niệm đơn vị chương trình (Unit) 3.1. Khái niệm đơn vị chương trình (Unit)

Thuật ngữ Unit trong ngôn ngữ lập trình Pascal được dịch là "đơn vị chương trình". Mỗi Unit được xem như một modul nhỏ chứa đựng một số công cụ cần thiết giúp cho người lập trình có thể dễ dàng thiết kế chương trình. Các Unit có thể gộp chung lại thành thư viện chương trình hoặc để phân tán trong một thư mục quy ước của Pascal .

Lệnh tham chiếu đến Unit được đặt ở đầu chương trình với cú pháp: USES tênunit; Các Unit được tổ chức trong Pascal dưới hai dạng:

* Các file độc lập với phần mở rộng là TPU (Turbo Pascal Unit), ví dụ GRAPH.TPU

* File thư viện chuẩn với phần mở rộng TPL (Turbo Pascal Library), ví dụ TURBO.TPL

Thư viện chuẩn của Pascal chứa đựng một số Unit cơ bản, hay được dùng đến, chúng được đóng gói lại và được để cùng chỗ với tệp khởi động TURBO.EXE.

3.2. Cấu trúc một Unit

Cấu trúc một UNIT bao gồm bốn phần cơ bản sau đây: a, Phần tiêu đề

Phần này chỉ gồm một dòng mở đầu là từ khoá UNIT sau đó là tên Unit, kết thúc dòng bằng dấu chấm phảy.

Unit tenUnit;

Tên Unit phải viết cách từ khoá Unit ít nhất một khoảng trống, các ký tự của tên phải viết liền nhau theo các quy định giống như tên chương trình, tên unit cần có tính chất gợi nhớ, không nên đặt tên quá dài .

b, Phần khai báo chung

Phần này bắt đầu bằng từ khoá INTERFACE

Bản thân từ khoá Interface có nghĩa là dùng chung, điều này quy ước rằng ở đây chúng ta phải khai báo các kiểu dữ liệu mới, các biến, hằng, hàm, thủ tục mà sau này các chương trình tham chiếu đến Unit sẽ sử dụng.

Phần nội dung bắt đầu bằng từ khoá IMPLEMENTATION. Tại đây chúng ta sẽ xây dựng nên các hàm, thủ tục mà tên của chúng đã được giới thiệu ở phần Interface. Việc xây dựng các chương trình con đã được giới thiệu ở phần lập trình cơ bản nên chúng ta không đề cập đến ở đây.

Chú ý:

• Những hàm và thủ tục chúng ta đã giới thiệu ở Interface thì bắt buộc phải được xây dựng ở đây bởi lẽ chúng chính là những gì mà các chương trình tham chiếu đến Unit cần sử dụng. Ngược lại trong phần này chúng ta có thể xây dụng các hàm và thủ tục khác hoặc khai báo các biến, hằng mới không có trong phần Interface, chúng được xem là phần riêng của Unit mà các chương trình tham chiếu đến Unit không được phép sử dụng.

d, Phần khởi động

Phần khởi động đặt giữa hai từ khoá BEGIN và END, sau End là dấu chấm giống như các chương trình Pascal bình thường. Phần khởi động là không bắt buộc phải có, trong trường hợp không có phần này thì chúng ta bỏ đi từ khoá BEGIN song vẫn phải có từ khoá END. để báo hiệu kết thúc Unit. Dưới đây là cấu trúc tổng thể của một Unit.

Unit Tên_Unit ; Interface ...

Uses Tên_Unit; (tên các Unit sẽ dùng trong các chương trình con của Unit này) Const .... (khai báo hằng)

Type ... (khai báo kiểu dữ liệu)

Var ... (khai báo biến cho các chương trình con trong Unit ) Tên các Procedure và Function của Unit

Implementation

(nội dung của từng chương trình con) Begin

Phần khởi tạo giá trị ban đầu (tuỳ chọn) End.

Về bản chất Unit cũng là một chương trình của Pascal, nó được xây dựng trên cơ sở các từ khoá và từ vựng mà Pascal đã thiết kế do vậy từ bên trong Unit chúng ta lại có thể tham chiếu đến các Unit khác không phân biệt là Unit chuẩn của Pascal hay Unit do người sử dụng tạo ra.

Ví dụ Xây dựng Unit HHP chứa hàm tính diện tích hình chữ nhật. Unit HHP;

Interface

Function dtcn(a,b: real): real; Implementation Function dtcn(a,b: real): real; Uses crt, dos; Var s: real; Begin

S:=a*b; Dtcn:=s; End;

4. Đồ họa và âm thanh

4.1. Đồ họa

a. Chế độ đồ hoạ (Graphic)

Unit Graph (Graph.tpu) chứa trên 50 chương trình con liên quan đến đồ hoạ. Để sử dụng chế độ đồ hoạ (Graphic Mode), cần có các tệp sau:

\TP\UNITs\Graph.tpu

\TP\BGI\*.bgi nhằm tương thích phần cứng và *.chr hỗ trợ cho phông chữ.

Trong chế độ đồ hoạ, màn hình được chia thành các điểm (Pixel). Mỗi điểm được xác định bởi cặp toạ độ (x,y). Điểm (0,0) là ứng với góc trên bên trái, điểm (GetMaxX, GetMaxY) ứng với góc dưới bên phải màn hình. Giá trị của GetMaxX và GetMaxY phụ thuộc vào Mode khởi tạo (InitGraph). Với các thẻ điều hợp hiển thị hiên nay, ta sử dụng tệp EGAVGA.BGI và phó mặc việc lựa chọn Mode cho chương trình thì nhận được độ phân giải là 640x480.

b. Cốt lõi của chương trình đồ hoạ

Chương trình sử dụng chế độ đồ hoạ sẽ có dạng:

uses Graph;

var grDriver, grMode, ErrCode: Integer; begin

grDriver:= DETECT;

InitGraph(grDriver, grMode, <PathTo EGAVGA.BGI>); ErrCode:= GraphResult;

if ErrCode = grOk then begin

{ Do graphics. Thực hiện các thao tác lập trình về đồ hoạ}

CloseGraph

end

else Writeln('Graphics error:', GraphErrorMsg(ErrCode)) end.

Trong đó, DETECT là hằng, <PathToEGAVGA.BGI> biểu thị (chỉ thị cho chương trình) đường dẫn tới tệp EGAVGA.BGI, GraphResult là hàm báo trạng thái khởi tạo, hằng grOK

thực chất là bằng 0. Hàm GraphErrorMsg(ErrCode: Integer) là xâu tương ứng với mã lỗi ErrCode, Thủ tục CloseGraph sẽ đóng chế độ đồ hoạ và trở về chế độ TEXT.

Chú ý: nếu chỉ vẽ hình thì trước câu lệnh CloseGraph, phải có lệnh dừng màn hình để có thể quan sát hình vẽ.

4.2. Các đối tượng đồ họa

Có thể tạm thời phân các đối tượng trong đồ hoạ thành 3 nhóm:

• Nhóm 1: Văn bản (TEXT)

• Nhóm 2: Điểm (Pixel), đoạn thẳng (Line), đường chữ nhật (Rectangle), đa giác (Poly), đường tròn (Circle), đường cung tròn (Arc), đường Ellipse.

• Nhóm 3 (có tô màu): Hình chữ nhật (Bar), hình Ellipse, hình quạt (Sector), quạt tròn (PiesLice), hình hộp (Bar3D).

Các đối tượng thuộc nhóm 1 và 2 không có phương thức tô màu (Fill).

4.3. Các thủ tục và hàm liên quan đến Đặt - nhận màu - nét và dáng điệu chữ.

GetMaxX: Integer: Giá trị cực đại của toạ độ X

GetMaxY: Integer: Giá trị cực đại của toạ độ Y

GetX: Integer: Giá trị toạ độ X hiện thời của con trỏ màn hình (không hiện).

GetY: Integer: Giá trị toạ độ Y hiện thời của con trỏ màn hình (không hiện).

GetPixel(X,Y: Integer):Word: Giá trị màu của điểm (X,Y).

TextHeight(TextString: string):Word: Giá trị chiều cao (Pixel) của TextString.

TextWidth(TextString: string): Word: Giá trị chiều rộng (Pixel) của TextString.

SetColor(Color: Word): Đặt màu(Color) cho nét vẽ, có hiệu lực cho đến khi đặt lại.

GetColor: Word: Hàm nhận lại màu nét vẽ đang được đặt.

GetMaxColor: Word: Giá trị cực đại của màu.

SetBkColor(ColorNum: Word): Đặt màu nền ColorNum cho các đối tượng.

GetBkColor: Word: Hàm nhận lại màu nền đang được đặt.

SetLineStyle(LineStyle: Word; Pattern: Word; Thickness: Word):

Đặt kiểu dáng (Style), mẫu (Pattern) và độ dày (Thickness) cho đường.

LineStyle:

SolidLn 0 Đường liền nét DottedLn 1 Đường chấm chấm

CenterLn 2 Đường gạch chấm Dashed Ln 3 Đường gạch gạch

UserBitLn 4 Đường người dùng định nghĩa

Thickness: NormWidth 1 Một nét ThickWidth 3 Ba nét

Pattern: Chỉ có nghĩa khi sử dụng UserBitLn. Pattern được định nghĩa bởi hai byte.

Ví dụ: nếu Pattern = $EBAC thì với NormWidth sẽ là 1110 1011 1010 1100, còn với ThickWidth sẽ là 1110 1011 1010 1100

1110 1011 1010 1100 1110 1011 1010 1100

GetLineSettings(var LineInfo: LineSettingsType)

Nhận lại sự đặt của SetLineStyle trước đó. LineSettingsType là bản ghi gồm ba trường là: LineStyle, Pattern, Thickness: Word;

SetTextStyle(Font, Direction: Word; CharSize: Word): Đặt lại phông (Font) chữ.

Font: phông (Font) chữ

DefaultFont 0 8x8 bit mapped font TriplexFont 1 Stroked font

SmallFont 2 Stroked font SansSerifFont 3 Stroked font GothicFont 4 Stroked font

Direction: Hướng trái sang phải hoặc hướng từ dưới lên. HorizDir 0 Orient left to right

VertDir 1 Orient bottom to top

CharSize: Cỡ chữ là 1, 2, 3, .. tuỳ theo loại font.

SetTextJustify(Horiz, Vert: Word): Đặt kiểu chữ

Horiz: (Horizontal) Vert: Theo chiều dọc (Vertical)

LeftText 0 BottomText 0

CenterText 1 CenterText 1

RightText 2 TopText 2

Nhận lại các giá trị đã đặt bởi hai thủ tục SetTextStyle vµ SetTextJustify. TextSettingsType là bản ghi gồm các trường: Font, Direction, CharSize, Horiz, Vert: Word.

SetFillStyle(Pattern: Word; Color: Word): Đặt màu tô (Color) và kiểu tô

(Pattern).

GetFillSettings(var FillInfo: FillSettingsType):

Nhận sự đặt lại của SetFillStyle. Bản ghi FillSettingsType có hai trường là:Pattern,Color: Word.

SetViewPort(x1,y1,x2,y2:Integer;Clip:Boolean):

Định nghĩa cửa sổ đưa ra.

Định nghĩa ViewPort tức là định nghĩa một vùng hình chữ nhật trên màn hình, (x1,y1) và (x2, y2) tương ứng là các toạ độ của góc trên bên trái và góc dưới bên phải, để từ đó trở đi (cho tới khi đặt lại), (x1, y1) được coi là (0, 0) trong cửa sổ đó.

Tham số Clip bằng True thì các phần hình vẽ bên ngoài cửa sổ sẽ không hiện ra, bằng False thì hiện ra.

GetViewSettings(var ViewPort: ViewPortType):

Nhận lại các giá trị được đặt bởi SetViewPort. ViewPortType là bản ghi gồm năm trường: x1, y1, x2, y2: Integer vµ Clip: Boolean.

CÁCH THỂ HIỆN CÁC ĐỐI TƯỢNG

PutPixel(X, Y: Integer; Color: Word): Vẽ điểm màu Color tại (X, Y).

MoveTo(X, Y: Integer): Đặt con trỏ màn hình vào điểm (X, Y).

MoveRel(Dx, Dy: Integer): Dịch con trỏ với độ lệch (Dx, Dy) so với vị trí hiện tại.

OutText(TextString: string): Hiển thị xâu TextString tại điểm hiện thời.

OutTextXY(X, Y: Integer,TextString: string): Hiển thị xâu TextString tại điểm (X, Y).

LineTo(X, Y: Integer): Vẽ một đoạn thẳng từ điểm hiện tại đến điểm (X, Y).

LineRel(Dx, Dy: Integer): Vẽ một đoạn thẳng từ điểm hiện tại đến điểm theo độ lệch (Dx, Dy).

Line(x1, y1, x2, y2: Integer): Vẽ một đoạn thẳng từ điểm (x1, y1) đến điểm (x2, y2).

Rectangle(x1, y1, x2, y2: Integer): Vẽ đường chữ nhật có toạ độ hai đỉnh đối diện: (x1, y1, x2, y2).

Arc (X,Y; Integer; StAngle, EndAngle, Radius; Word): Vẽ cung tròn tâm (X, Y), góc bắt đầu là StAngle (độ), góc kết thúc là EndAngle (độ) , bán kính bằng Radius.

Ellipse(X, Y: Integer; StAngle, EndAngle: Word; XRadius, YRadius: Word): Vẽ Ellipse tâm (X, Y), góc bắt đầu là StAngle (độ), góc kết thúc là EndAngle (độ) , các bán trục tương ứng là XRadius và YRadius.

DrawPoly(NumPoints: Word; var PolyPoints): Vẽ đa giác PolyPoints với số đỉnh là

Một phần của tài liệu Ngôn ngữ lập trình Pascal ĐH Hoa Lư (Trang 119)

Tải bản đầy đủ (DOC)

(141 trang)
w