Kiểu Procedure là kiểu dữ liệu mà một đối tượng biến chẳnghạn mang kiểu này nhận giá trị không phải là các giá trị kiểu cơbản mà là một “chương trình con”.. ĐỊNH NGHĨA KIỂU PROCEDURE Để
Trang 1Phần 1
KIỂU DỮ LIỆU PROCEDURE
I KHÁI NIỆM - ĐỊNH NGHĨA KIỂU PROCEDURE
1 KHÁI NIỆM
Ngôn ngữ Pascal đã cung cấp cho chúng ta các kiểu dữ liệu từ đơngiản đến nâng cao như tập tin, con trỏ, Bây giờ ta lại làm quen vớimột kiểu dữ liệu mới gọi là kiểu Procedure.
Kiểu Procedure là kiểu dữ liệu mà một đối tượng (biến chẳnghạn) mang kiểu này nhận giá trị không phải là các giá trị kiểu cơbản mà là một “chương trình con” Chúng ta có thể xem một chươngtrình con như là một giá trị của một biến để rồi thực hiện cácphép gán hay truyền vào một chương tình con khác thông qua các thamsố kiểu Procedure này
Thật chất một biến kiểu Procedure chính là một con trỏ chứa đại chịsegment và offset của một chương trình con thay vì địa chỉ của mộtvùng nhớ chứa dữ liệu
2 ĐỊNH NGHĨA KIỂU PROCEDURE
Để định nghĩa kiểu Procedure chúng ta phải dùng từ khóaPROCEDURE hay từ khóa FUNCTION và khai báo các đối số cũng nhưkiểu của chúng mà con trỏ trỏ đến Procedure hay Function nào đó.Cú pháp như sau
TYPE
<tên_kiểu> = PROCEDURE( <đối_số>: <kiểu> );
<tên_kiểu> = PROCEDURE( VAR <đối_số>: <kiểu> );
<tên_kiểu> = FUNCTION( <đối_số>: <kiểu> ):
<kiểu>;
Ví dụ:
TYPE
AnyProc = PROCEDURE;
DisplayProc = PROCEDURE(S: STRING);
StrProc = PROCEDURE(x,y,Attr:Byte; VAR St: STRING);StrFunc = FUNCTION(A: Real; B,C: Byte): STRING;
Trang 2II.KHAI BÁO BIẾN PROCEDURE
Khai báo biến kiểu Procedure cũng bình thường như các biến thuộckiểu khác, bắt đầu bằng từ khóa VAR, khai báo tên biến và tênkiểu Procedure
Chú thích cho ví dụ trên:
• Proc là một biến con trỏ trỏ đến các chương trình con dạng
Procedure không có đối số
• DProc là một con trỏ trỏ đến các Procedure có đối số kiểu chuỗi.
• SProc là một con trỏ trỏ đến các Procedure có bốn đối số với ba đối số đầu kiểu Byte, đối số thứ tư kiểu chuỗi.
• DFunc là một con trỏ trỏ đến các Function có ba đối số mà hai đối số đầu có kiểu Byte, đối số thứ ba kiểu Real Các Function
này sẽ trả về một giá trị kiểu chuỗi
Chú ý: Các biến trỏ trỏ vào Procedure sẽ hoạt động như một câu
lệnh trong chương trình, tức là như một lời gọi thủ tục, trong ví dụ
trên là các biến Proc, DProc, SProc Tương tự vậy, các biến trỏ đến
các Function sẽ hoạt động như một hàm, tức là chúng xuất hiệndưới dạng các lời gọi hàm Tóm lại các biến kiểu Procedure xuấthiện trong chương trình như là các lời gọi CTC với đầy đủ các thamsố mà Procedure hay Function mà nó trỏ đến có thể có
Các hàm và thủ tục sau này được xem như là các giá trị của cácbiến kiểu Procedure phải là các chương trình con được khai báo gọi xavới chỉ thị {$F+}.
Trang 3III CÁC PHÉP TOÁN TRÊN KIỂU PROCEDURE
Ví dụ: Viết chương trình thực hiện một trong bốn phép toán + - × và
÷ bằng cách dùng các hàm và sử dụng biến kiểu Procedure
Trang 4{ Gán giá trị vào biến Procedure }
Chú thích cho chương trình ví dụ:
Trong chương trình trên, chúng ta đã định nghĩa một kiểu Procedure là
FuncType là hàm có hai tham số kiểu Real và giá trị trả về cũng là kiểu Real.
FuncType = Function( x, y: Real ): Real;
Biến FuncVar có kiểu FuncType là một biến sẽ chứa các tên hàm
có danh sách đối số và kiểu trả về của hàm như là đã khai báo
Trong ví dụ trên là bốn hàm Cong, Tru, Nhan và Chia trả về kết quả
là phép toán tương ứng tác động lên hai đối số Tiếp thao là phép
gán các tên hàm cho biến FuncVar ứng với các giá trị chủa Ch Và cuối cùng là hình thức xuất hiện của FuncVar trong câu lệnh.
WriteLn(‘Kết quả là: ’, FuncVar(x, y):2:2);
Đối số của CTC có kiểu Procedure
Kiểu dữ liệu Procedure cũng có thể dùng làm kiểu của các đối sốCTC Một đối số kiểu Procedure cũng hoạt động đúng theo các nguyêntắc mà các đối số mang kiểu khác hoạt động
Trang 5là kiểu logic (Boolean) Để lấy giá trị địa chỉ của một biến chúng tadùng toán tử @.
@<Biến_Procedure_1> = hay <> @<Biến_Procedure_2>
Ví dụ: Chúng ta không thể viết
If TestFunc <> StrFunc then
Mà phải viết
If @TestFunc <> @StrFunc then
Chú ý: Để mô tả biến Procedure không trỏ vào vùng nhớ nào,
chúng ta phải gán con trỏ NIL vào con trỏ trỏ đến biến Procedure
Ví dụ: @TestFunc := NIL;
Đối số của CTC có kiểu Procedure
ABC
DEF
Trang 61 Chúng ta cần phân biệt giữa lời gọi hàm và lệnh gán giá trị làtên chương trình con cho biến Procedure.
Ví dụ: Xét chương trình sau:
TYPE StrFunc = Function: STRING;
WriteLn(‘Thực hiện hàm TrMark’);
TrMark := ‘Advanced PASCAL’;
End;
{$F-}
BEGIN
st := TrMark; {Lời gọi hàm TrMark }
pF := TrMark; {Gán đ/chỉ của TrMark cho pF }
END
2 Các và thủ tục được xem như là một kiểu dữ liệu cho biếnProcedure phải có các yêu cầu sau:
• Phải là hàm và thủ tục của NSD
• Phải khai báo gọi xa với cặp chỉ thị {$F+} và {$F-}
• Không có khai báo Interrupt và câu lệnh Inline.
• Không có các CTC lồng bên trong
Trang 7Phần 2
CHUYỂN ĐỔI KIỂU – UNIT NGƯỜI
DÙNG
IV CHUYỂN ĐỔI KIỂU (TYPE CASTING)
Type Casting là một kỹ thuật dùng để biến đổi kiểu của một vùngnhớ máy tính Pascal là một NNLT rất chặc chẽ trong trong vấn đềtổ chức dữ liệu, nhất là trong các phép gán hay trong biểu thức Sựchặc chẽ này của Pascal đôi khi lại gây trở ngại cho lập trình viên vìphải sử dụng nhiều lệnh mới đạt được mục đích Lấy một ví dụ
như sau, St là một biến kiểu chuỗi và đang lưu giữ chuỗi ‘Turbo’, gải
sử chúng ta muốn tìm mã ASCII của các ký tự trong chuỗi thì phảidùng như sau:
VAR St: String[10];
St:= ‘Turbo’;
For i:=1 to Length(St) do Write(Ord(St[i],#32);
trong đó chúng ta đã sử dụng đến 2 lời gọi hàm, hàm Length và Ord để xác định chiều dài chuỗi và lấy mã ASCII của ký tự trong
TYPE ByteArr = Array[0 10] of Byte;
VAR A: ByteArr;
A[0]:=5; A[1]:=84; A[2]:=7;
A[4]:=114; A[5]:=98; A[6]:=111;
Vùng nhớ biến A sẽ là:
Trang 80 1 2 3 4 5 6 7 8 9 10
Nếu ta xem vùng nhớ trên là một chuối thì nó sẽ trở thành chuỗi
‘Turbo’, ngược lại với vùng nhớ của St ta có thể xem đó là một mảng các số kiểu Byte Cách xem xét như vậy gọi là chuyển đổi kiểu (Type Casting), mà thực chất là tái định nghĩa lại kiểu dữ liệu
của một vùng nhớ
Hạn chế của việc đổi kiểu mà không thể không nhắc đến là nósẽ làm khái niệm về khả năng kiểm soát kiểu dữ liệu trongchương trình và đôi lúc xảy ra những điều đáng tiếc
3 CÚ PHÁP ĐỔI KIỂU
Cú pháp:
<Kiểu> ( <Giá_trị> )
Ví dụ 1: ch := CHAR(65);
Thanhtien:= (Tieuthu – 100)*LongInt(500) + 100*250;
4 RECORD HÓA MỘT VÙNG NHỚ
Một ví dụ thực tế: Khi muốn tạo ra một cửa sổ trong chế độ văn bản bằng thủ tục Window(x2, y1, x2, y2) tọa độ đỉnh trái trên và phải dưới phải được lưu giữ trong hai biến kiểu Word là:
WindMin Tọa độ đỉnh trái trên
Turbo
Trang 9WindMax Tọa độ đỉnh phải dưới
Byte cao Byte thấp
Để lấy các giá trị x1, y1, x2, y2 từ hai biến WindMin và WindMax, chúng ta cũng có thể dùnh hai hàm Lo và Hi của DOS, nhưng ở đây
chúng ta thực hiện kỹ thuật Record hóa vùng nhớ 2 byte này bằngmột kiểu Record, chẳng hạn:
TYPE
Toado = RECORD
x, y: Byte;
END;
Thứ tự x, y trong record phải theo thứ tứ, trường nào đứng trước sẽ
có địa chỉ thấp hơn trường đứng sau
Sau đó ta thực hiện record hóa, thực ra đó là một dạng khác của
Type Casting, hai biến WindMin và WindMax và lấy các giá trị như sau:
Turbo Pascal 5.0 trở lên cung cấp một khả năng nữa đó là có thểkhai báo tham biến không kiểu, điều này thực sự là hữu ích Vídụ, để viết chương trình con nhập một số nguyên có kiểm tra lỗi,
nhưng số nguyên thì kiểu cụ thể là Byte? hay Integer? hay Word?, Lúc
này ta phải viết nhiều CTC để nhập cho một kiểu cụ thể nếu khôngdùng đối sô không kiểu
Ví dụ: Chương trình con nhập số nguyên.
PROCEDURE ReadInt( x, y: Byte; VAR N );
Var Temp: STRING[80]; Result: Integer;
Trang 10Thủ tục này có sử dụng kỹ thuật Type Casting với tham biến N để
sao cho nó làm việc OK với mọi trường hợp, điều này là khôngbắt buộc trong thủ tục này
Bây giờ chúng ta hãy thử viết chương trình để nhập các biến kiểu
nguyên ShortInt, Integer, LongInt, Byte, Word sử dụng thủ tục ReadInt
Một lý do để ta tạo các Unit của NSD là: khi chương trình quá lớn,có những nhóm chương trình con và các dữ liệu hoàn chỉnh, có thểđược sử dụng bởi nhiều chương trình khác nhau Vì vậy ta có thểnhóm những chương trình con các cấu trúc dữ liệu trên thành cácUnit Làm như thế sẽ gia tăng tối đa tốc độ chương trình, và cácchương trình con đó có thể được sử dụng bởi những chương trình
khác, bằng cách “USES” unit đó ra.
Để sử dụng Unit trong chương trình con thì ta khai báo các Unit đó sautừ khóa USES.
Ví dụ: USES Crt, Dos, Printer, NewUnit;
Tóm lại, ý nghĩa khi tạo unit là như sau:
• Ghi lại các CTC thường dùng vào một module tạo thư viện chươngtrình
• Phân chương trình lớn thành các module nhỏ hơn
• Tính có thể sử dụng lại của unit
7 CÁC THÀNH PHẦN CỦA MỘT UNIT
Một unit bao gồm các thành phần sau:
i.Phần Khai báo (UNIT )
ii Phần Giao tiếp (INTERFACE)
iii Phần Cài đặt (IMPLEMENTATION)
iv Phần Khởi tạo (INITIALIATION)
Trang 11Các phần được sắp xếp như sau trong một file nguồn:
UNIT <Tên_Unit>; {Tên Unit phải đ ût giống với tên fileă }USES <DOS_Các Unit cần dùng>;
{$F+}
INTERFACE
(* Khai báo tên các thủ tục và hàm, tên biến, kiểu dữ liệuđược viết đầy đủ trong phần Implementation sau Tên các thủtục: khai báo hàm trong phần này là tên các chương trìnhcon, biến và kiểu dữ liệu mà các Unit, trong chương trìnhgọi Unit này cần dùng đến *)
; {khai báo tên hàm của Unit }
[BEGIN ]
END
• Phần Khai báo
Gồm tên unit viết sau từ khóa UNIT và phải trùng trên với tên file
nguồn Đây là tên mà sau này sẽ sử dụng
Ví dụ: UNIT UEXAM;
• Phần Giao tiếp
Còn được gọi là phần Public, là phần giao diện của unit với bênngoài Những gì khai báo trong phần này thì được các chương trìnhsử dụng unit này có thể gọi đến Phần này bắt đầu bằngtừ khóa INTERFACE
Trang 12PROCEDURE ThiDu1 (Var x, y: Real);
PROCEDURE ThiDu2 (S: String);
• Phần Cài đ ă ût
Dùng để chứa các mục riêng (private) cho các câu lệnh công cụnằm trong unit, là phần cài đặt cụ thể các CTC đã được khaibáo trong INTERFACE Phần này bắt đầu bằng từ khóa
IMPLEMENTATION sau đó là các khai báo unit sử dụng, hằng,
kiểu, biến, Phần này là “tài sản riêng” của unit, các nơi sửdụng unit này không thể dùng chúng
Ví dụ:
IMPLEMENTATION {Phần Cài đ ûtă }
Var x, y: real; {các biến chỉ dụng trong Unit này }
PROCEDURE ThiDu1 (Var x, y: real);
Thư mục chứa UNIT
Pascal thông thường sẽ tìm kiếm các unit trong thư mục mặc địnhđược chỉ bởi NSD Để làm điều này chúng ta thực hiện lệnh sauđể chỉ rõ vị trí của unit
Options - Directories, trong mục Unit Directories đưa vào các đường
dẫn tìm kiếm Nếu không tìm thấy Pascal sẽ tìm trong TM hiện thời
8 VÍ DỤ ÁP DỤNG
Chúng ta thử tạo Unit UEXAM được lưu dưới file UEXAM.PAS Nội dung:
UNIT UEXAM; {Phần Khai báo }
Trang 13PROCEDURE ThiDu1 (Var x, y: Real);
PROCEDURE ThiDu2 (S: String);
FUNCTION TenF (i, j: integer):string;
{$F-}
IMPLEMENTATION {Phần Cài đ ûtă }
Var x, y: real; {các biến chỉ dụng trong Unit này }
PROCEDURE ThiDu1 (Var x, y: real);
Ini:= 'Hello, World!';
END.
Chữ BEGIN ở dòng 29 có thể có hoặc không, nếu không có dòng
29 thì dòng 30 sẽ phải không có
Các khai báo tên từ dòng 5 đến dòng 8 là các tên biến và chươngtrình con có thể được dùng đến của Unit khi dịch file trên (Chế độ ghiđĩa), Turbo pascal sẽ tạo file UEXAM.TPU
Biên dịch UNIT
• Chọn chế độ biên dịch ghi đĩa: Compile - Destination → Disk.
• Biên dịch: Bấm tổ hợp phím Alt - F9 hoặc F9.
Tập tin biên dịch được sẽ có phần mở rộng là TPU
Trang 149 CÁC UNIT CÓ DÙNG UNIT KHÁC
Bất kỳ một unit nào cũng có thể sử dụng các unit khác bằngcách đưa vào khai báo với USES Có hai cách sử dụng unit khác:
i.Đưa sau từ khóa INTERFACE
Ví dụ:
UNIT Chuoi;
INTERFACE
Uses Crt;
Function Upper( ): STRING;
Function Lower( ): STRING;
Function Upper( ): STRING;
Function Lower( ): STRING;
IMPLAMENTATION
Uses Crt;
Lưu ý:
• Không nên tổ chức các unit tham chiếu vòng
Ví dụ: unit A tham chiếu unit B
unit B tham chiếu unit A
• Khi nhiều unit tham chiếu cùng một unit thì có một bản sao cảu unitđược tham chiếu đó được nạp vào bộ nhớ
Ví dụ: unit A tham chiếu unit C
unit B tham chiếu unit C
thì chỉ một bản sao của unit C được nạp vào bộ nhớ
Sự va chạm tên các tiện ích (facility) giữa các unit
Trong quá trình xây dựng các unit thì có thể xảy ra trường hợp cácunit chứa các tiện ích có tên giống nhau, ví dụ unit A có chứa thủ tụ
ClrScr và unit CRT có chứa thủ tục ClrScr Chúng ta vẫn có thể sử
dụng hai tiện ích này của hai unit này mà không sợ nhầm lẫn
Ví dụ:
PROGRAM Test;
USES A, CRT;
Trang 15.
Trong trường hợp này, thủ tục trong unit được khai báo sau được ưu
tiên hơn, nghĩa là thủ tục ClrScr của unit CRT sẽ được dùng.
Để sử dụng hai thủ tục này một cách độc lập, chúng ta sử dụngcú pháp sau:
1 Phân đoạn mã (Code Segment)
Code Segment dành cho các unit nếu CT có khai báo USES
2 Phân đoạn dữ liệu (Data Segment)
3 Phân đoạn Stack (Stack Segment)
Chứa địa chỉ trở về cho các lệnh gọi thủ tục, hàm, các biếncục bộ được khai báo bên trong các xử lý đó
4 Heap: chứa dữ liệu của các loại biến con trỏ
Ở chế độ REAL kích thước của một segment là 64K, riêng vùngheap là vùng bộ nhớ còn lại
Thực ra còn có một cấu trúc khác trước Code Segment của CT làPSP (Program Segment Prefix) Nó được phát sinh bởi DOS mỗi khi nạpmột tập tin EXE vào bộ nhớ DOS đặt vào đó những thông tin mànó cần để quản lý chuơng trình
Ví dụ:
PROGRAM UnitSexp;
USES unitA, unitB, unitC;
Trang 16Bố trí trong bộ nhớ Kích thước
Near và Far
Tất cả các CTC toàn cục được khai báo trong phần INTERFACE làFAR Các trình xử lý dạng Far có thể được gọi bất kỳ nơi nào trongchương trình, thậm chí trong các phân đoạn chương trình khác
Tất cả các CTC cục bộ được khai báo trong phần IMPLEMENTATIONcộng với những CTC trong choong trình mẹ là các chương rình xử lýdạng NEAR (Khi được gọi các trình xử lý dạng Near sẽ đẩy vào Stackđịa chỉ offset) Chúng ta chỉ có thể gọi các trình xử lý dạng Near từbên trong phân đoạn chương trình đó mà thôi
11 CÀI ĐẶT UNIT VÀO CÁC TẬP TIN THƯ VIỆN
Trong khi khởi động, Turbo Pascal tự động nập một số unit thườnglàm việc được ghi trong tập tin TURBO.TPL như SYSTEM.TPU, CRT.TPU,PRINTER.TPU, Người sử dụng có thể thêm hay bớt các unit trongtập tin TURBO.TPL này Công cụ sử dụng được cung cấp bởi Borlandlà TPUMOVER.EXE
• Hiển thị các unit trong một thư viện (TPL - Turbo Pascal Library)
Cú pháp: TPUMOVER <Lib_File> ↵
Ví dụ: TPUMOVER TURBO.TPL ↵
sẽ cho kết quả:
Trang 17• Thêm/Xóa/Tách unit từ thư viện
option có thể là
+ : thêm unit vào thư viện
- : xóa unit khỏi thư viện
* : tách unit khỏi thư viện
? : liệt kê các lệnh của TPUMOVER
Ví dụ:
- Thêm unit Math vào TURBO.TPL
TPUMOVER TURBO /+ MATH ↵
- Xóa unit Turbo3 khỏi TURBO.TPL
TPUMOVER TURBO /- TURBO3 ↵
- Tách unit Graph3 khỏi TURBO.TPL và tạo thành file GRAPH3.TPU
TPUMOVER TURBO /* GRAPH3 ↵
Phần 3
TRUY XUẤT VÙNG NHỚ MÁY TÍNH
Trang 18VI BIẾN TRUY XUẤT VÙNG NHỚ
12 BIẾN ĐỊA CHỈ TUYỆT ĐỐI
Các biến được khai báo trong chương trình sau từ khóa VAR hay các
biến động tạo ra bằng thủ tục New hay GetMem đều do máy tính
chủ động sắp xếp vị trí của chúng trong bộ nhớ
Biến địa chỉ tuyệt đối là biến là người lập trình có thể quy định địachỉ của chúng với từ khóa ABSOLUTE
Cú pháp:
VAR <Biến>: <Kiểu> ABSOLUTE <Seg:Ofs>;
Đặt trùng địa chỉ một biến khác đã khai báo
VAR <Biến>: <Kiểu> ABSOLUTE <Tên_biến>;
Ví dụ 1: Sử dụng biến tuyệt đối để lấy độ dài chuỗi mà không cần hàm Length hay truy xuất ký tự thứ 0 Hàm RPad sẽ bổ sung các ký tự trắng bên phải để chuỗi có chiều dài là N.
Function RPad(St: String; N: Byte): String;
Var Len: Byte ABSOLUTE St;
Ví dụ 2: Dùng biến tuyệt đối để lấy mode màn hình hiện tại Giá
trị mode màn hình là giá trị 1 byte ở địa chỉ $0000:$0449
VAR Mode: Byte ABSOLUTE $0:$0449;