Tải bản đầy đủ (.doc) (27 trang)

Chuyen de BD HSG 3

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (359.45 KB, 27 trang )

TÌM HIỂU CÁCH SỬ DỤNG CÁC CHỨC NĂNG CỦA
Borland Pascal 7.0
Borland Pascal là công cụ phát triển các ứng dụng trên DOS của hãng Borland với ngôn ngữ
lõi là PASCAL. Nó bao gồm các đơn thể (module) chính sau:
• Trình biên dịch (Compiler) có chức năng dịch chương trình ra mã máy để chạy
• Trình soạn thảo (Editor) cho người lập trình sử dụng viết chương trình
• Trình gỡ rối (Debugger) cho phép truy tìm và phát hiện lỗi
• Trình trợ giúp (Helper)
I. SƠ LƯỢC VỀ GIAO DIỆN
1
BORLAND PASCAL 7.0 (BP7)
Giao diện của BP7 gồm có: Hệ thống menu, thanh trạng thái, các cửa sổ làm việc. Mỗi cửa sổ
có một chức năng, có tên, số hiệu riêng, Nếu là cửa sổ soạn thảo chương trình thì tên cửa sổ
chính là tên file chương trình. Tại một thời điểm có thể có nhiều cửa sổ nhưng chỉ có duy nhất
một cửa sổ được quan tâm nhất (Active Window
2
), cửa sổ này có khung viền đôi còn các cửa
sổ khác có khung viền đơn.
1
Giao diện hay giao diện người - máy (User-Machine Interface) là một cách thức đối thoại giữa người và máy để
cho người sử dụng tiện lợi trong việc đưa yêu cầu của mình cho máy tính xử lý và dễ dàng nhận lại các kết quả
ra. Hiện có nhiều kiểu giao diện nhưng phổ biến nhất là giao diện dòng lệnh Command Line (như
Command.Com của DOS), giao diện menu (như Borland Pascal 7), giao diện GUI (như Windows hay Apple
Macintosh)
2
Gọi là Active Window- Cửa sổ hoạt động hơi thiếu chính xác, tất cả các cửa sổ đều hoạt động, nhưng chỉ có
một cửa sổ nhận các tín hiệu bàn phím mà thôi (chính vì vậy mà mở nhiều cửa sổ sẽ làm tốc độ chậm) người ta
có một từ chính xác hơn: Focus Window- cửa sổ nổi bật, Nhưng đây là thuật ngữ của nhà sản xuất, ta không nên
thay đổi.
1
II. HỆ THỐNG MENU


Hệ thống menu của BP7 có nhiều menu xếp theo thứ bậc, mỗi menu tương ứng với một thao
tác hoặc lại mở ra một danh mục menu mới. Mỗi menu có thể có một ký tự trong đó gọi là
Shortcut (tôi không biết nên dịch là gì cho hợp lý) và có thể được gán cho một phím gọi là
Shortcut Key. Ví dụ như menu con Save nằm trong menu File có Shortcut là chữ S và
Shortcut Key là phím <F2>, trên màn hình giao diện BP7, ta sẽ thấy chữ S trong menu Save
và chữ F trong menu File có màu khác với các chữ khác. Để gọi một chức năng trong hệ
thống menu, ta có nhiều cách:
• Cách 1: Là nếu đang ở cửa sổ soạn thảo, ấn F10 để chuyển lên thanh menu sau đó dùng
các phím ←, → để chọn, chọn xong bấm <Enter> sẽ hiện ra danh mục menu con, dùng
các phím ↑, ↓ chọn tiếp và bấm Enter; hoặc bấm trực tiếp Shortcut của menu con cần
chọn.
• Cách 2: Là nếu đang trong cửa sổ soạn thảo, ta bấm <Alt> + Shortcut của menu cần chọn
trên thanh menu thì cũng mở ra menu con tương ứng, chọn tiếp như trên.
• Cách 3: Là nếu một menu con có gán Shortcut key thì ta bấm trực tiếp Shortcut key để
thực hiện chức năng tương ứng với menu đó. Theo kinh nghiệm của tôi, cách này là nhanh
và tiết kiệm thời gian nhất, chính vì vậy ta cố gắng nhớ được càng nhiều Shortcut key
càng tốt. Dưới đây là một số chức năng hay dùng và Shortcut key của chúng:
Chức năng menu tương ứng Shortcut key
Mở file đã có trên đĩa File/ Open F3
Ghi file đang soạn vào đĩa File/ Save F2
Đóng cửa sổ đang hoạt động Window/ Close Alt + F3
Phóng to cửa sổ hoạt động lên tối đa Window/ Zoom F5
Thay đổi vị trí, kích thước cửa sổ Window/ Size/Move Ctrl + F5
Thay đổi cửa sổ hoạt động Window/ Next F6
Chạy chương trình đến vị trí con trỏ thì dừng lại Run/ Go to Cursor F4
Gỡ rối, chạy theo vết vào chương trình con Run/ Trace into F7
Gỡ rối, chạy từng bước Run/ Step over F8
Chạy chương trình Run/ Run Ctrl + F9
Thêm biến vào cửa sổ theo dõi giá trị (Watches) Debug/ Add watch Ctrl + F7
Phục hồi lại trạng thái văn bản về trạng thái trước

khi thực hiện một số thao tác gần nhất
Edit/Undo Alt + BackSpace
Xem kết quả chương trình kết xuất ra màn hình Debug/ User screen Alt + F5
Dịch thử, kiểm tra lỗi cú pháp trong một chương
trình ở một cửa sổ, có nhiều cách khác nhau,
nhưng trong trường hợp chương trình không quá
dài thì dùng chức năng Make là tiện nhất
Có nhiều cách:
1. Compiler/ Compiler
2. Compiler/ Make
3. Compiler/ Build
Alt + F9
F9
2
II.1. F ile menu (Alt + F): Các chức năng về đĩa, thư mục, file,
• N ew: Tạo cửa sổ mới để soạn chương trình, cửa sổ soạn thảo mới sẽ có tên là
NONAMExx.PAS (xx là một số từ 00 đến 99) và mặc nhiên trở thành cửa sổ hoạt động
(Active Window), nằm trên tất cả các cửa sổ đang mở.
• O pen : Mở khung đối thoại "Open a file" cho người sử dụng chọn một file, nếu file này
đã có trên đĩa thì nội dung sẽ được đưa vào một cửa sổ soạn thảo mới, nếu file chưa có
trên đĩa thì cửa sổ mới sẽ là cửa sổ rỗng; từ đó có thể soạn tiếp chương trình.
• S ave: Ghi văn bản trong cửa sổ hoạt động vào đĩa, nếu chưa đặt tên file, BP7 sẽ hỏi tên
file như Save as
• Save as: Ghi văn bản trong cửa sổ hoạt động vào đĩa với một tên khác, trước khi ghi, máy
sẽ hiện khung đối thoại "Save File as" để chọn tên file mới.
• Save all: Ghi tất cả văn bản trên các cửa sổ đang mở vào đĩa.
• C hange dir : Thay đổi thư mục hiện hành
• P rint: In văn bản chương trình ra máy in
• Printer setup: Đặt các thông số cấu hình máy in
• D OS shell: Tạm thời thoát về dấu nhắc DOS (VD: C:\>), vẫn giữ lại BP7 trong bộ nhớ,

khi cần có thể quay trở về BP7 bằng cách gõ EXIT tại dấu nhắc.
• Exit: Đóng tất cả các cửa sổ, thoát khỏi Borland Pascal, giải phóng bộ nhớ chiếm dụng,
trở về DOS.
II.2. E dit menu (Alt + E): Các chức năng soạn thảo
3
• U ndo: Phục hồi lại trạng thái văn bản về trạng thái trước khi thực hiện một số thao tác gần
nhất, rất hữu ích trong việc phục hồi lại các đoạn văn bản bị xoá nhầm, sửa nhầm vì một
lý do nào đó
• R edo: Ngược lại với Undo: Nói cách khác nó Undo lại trạng thái trước khi Undo.
• Cut: Cắt đoạn văn bản đã đánh dấu vào Clipboard (một vùng nhớ đặc biệt của BP7)
• C opy: Sao chép đoạn văn bản đã đánh dấu vào Clipboard, khác với Cut, đoạn văn bản
đánh dấu không bị cắt bỏ
• P aste: Dán đoạn văn bản trong Clipboard vào vị trí con trỏ
• Clear: Xoá đoạn văn bản đã đánh dấu.
• S how clipboard: Hiện nội dung văn bản trong Clipboard (có thể sửa trong đó). (Clipboard
có thể quan niệm như một vùng nhớ phụ của BP7 để lưu các đoạn văn bản tạm thời cho
việc cắt dán)
Lưu ý: BP7 khuyến cáo nên sử dụng phương pháp đánh dấu đoạn văn bản bằng cách giữ Shift
+ các phím ←↑→↓, sau đó có thể sao chép, di chuyển khối đánh dấu thông qua Clipboard
(Copy-Paste; Cut-Paste) để có thể sao chép một đoạn văn bản từ cửa sổ này sang cửa sổ khác.
Tuy nhiên nếu như việc cắt, dán, sao chép là trên cùng 1 cửa sổ thì người ta vẫn ưa chuộng
cách thức của TP5 hơn: Copy khối đánh dấu tới vị trí con trỏ: Ctrl + K+C, Di chuyển khối
đánh dấu tới vị trí con trỏ: Ctrl + K+V, Xoá khối đánh dấu: Ctrl + K+Y, Ghi khối đánh dấu
vào một file: Ctrl + K+W, Đọc một file vào vị trí con trỏ và tạo khối đánh dấu chứa nội dung
đọc vào: Ctrl + K+R. Dịch các dòng chứa khối vào trong: Ctrl + K + I. Dịch các dòng chứa
khối ra ngoài Ctrl + K + U.
II.3. S earch menu (Alt + S): Tìm kiếm
• F ind : Tìm một dãy ký tự trong văn bản; BP7 sẽ hiện khung thoại hỏi xâu cần tìm và cho
phép chọn cách tìm: Tìm có phân biệt chữ hoa, chữ thường; Tìm trong cả văn bản hay
phần đánh dấu; Tìm xuôi xuống dưới hay tìm ngược lên trên v.v Nếu tìm được, BP7 sẽ

cho con trỏ nhảy tới vị trí của xâu ký tự tìm thấy. Shortcut key của menu này là Ctrl+Q+F
• R eplace : Tìm và thay thế; Gần giống như Find nhưng thêm chức năng: thay thế dãy ký
tự tìm được bởi dãy ký tự mới, Shortcut key: Ctrl+Q+A.
• S earch again: Lặp lại thao tác Find hay Replace dùng gần đây nhất.
4
• G o to line number Cho người sử dụng gõ số hiệu dòng và nhảy con trỏ tới dòng đó.
• Show last compiler error: Cho hiện thông báo lỗi sau nhất, ít công dụng
• Find error : Tìm lỗi thông qua địa chỉ lỗi, ít công dụng
• Find procedure : Gõ tên thủ tục hay hàm, BP7 sẽ nhảy con trỏ tới thân chương trình con
đó. Rất hữu ích trong một chương trình dài có rất nhiều chương trình con.
• Previous browser: hiện cửa sổ browser mới mở gần đây nhất
• O bjects: Hiện các thực thể đối tượng trong chương trình (Dùng cho lập trình hướng đối
tượng)
• U nits: Hiện tất cả các unit và program đang sử dụng.
• Global: Hiện tất cả các đối tượng toàn cục (hằng, biến, chương trình con), chương trình có
thể sử dụng mà không cần khai báo (ví dụ: MaxInt, Abs, ).
• Symbol : Hiện thông tin về một ký hiệu nào đó trong chương trình trong cửa sổ Browser.
II.4. R un menu (Alt + R): Chạy chương trình
• R un: Chạy chương trình. Thông thường sẽ chạy chương trình đang soạn thảo trong cửa sổ
hoạt động. Nếu đặt Primary file (trong Compiler menu) thì sẽ chạy Primary file.
• S tep over: Chạy từng bước để gỡ rối, mỗi lần Step over, chương trình cứ chạy xong 1
dòng lệnh thì dừng lại.
• T race into: Gần giống như Step over, chỉ có điều nếu như chạy 1 dòng lệnh có lời gọi
chương trình con, nó sẽ chạy theo vết vào chương trình con đó.
• G o to cursor: Chạy đến chỗ con trỏ thì dừng lại, chẳng hạn chương trình có 7000 dòng và
ta muốn chạy ngay tới dòng 5000 để gỡ rối tiếp, thay vì bấm hàng ngàn lần F8 để Step
over, ta có thể di chuyển đến dòng 5000 và bấm F4.
• P rogram reset: Nếu chương trình chưa chạy hết bị dừng lại do nhiều nguyên nhân: người
sử dụng ấn Ctrl+Break hay do chạy bằng Step over hay Trace into thì có thể dùng chức
năng này dừng vô điều kiện việc chạy tiếp.

• Parameters: Giả lập tham số dòng lệnh như khi chạy file .EXE
II.5. C ompiler menu (Alt + C): Dịch chương trình
Borland Pascal và Turbo Pascal có khác nhau đôi chút về menu này
Đối với Borland Pascal
5
• C ompiler: Dịch chương trình trong cửa sổ hoạt động, mặc nhiên nếu chạy mà chưa dịch
thì BP7 sẽ tự động dùng chức năng này để dịch trước rồi mới chạy
• M ake: Dịch chương trình nhưng đồng thời dịch lại các thư viện sử dụng nếu có sự thay
đổi.
• B uild: Dịch chương trình, đồng thời dịch lại tất cả các thư viện mà chương trình sử dụng
• T arget : Mặc định là Real: Chế độ thực: Chương trình viết ra để chạy trong bộ nhớ quy
ước của DOS với giới hạn bộ nhớ 640KB, có hai chế độ khác: Chế độ Protected cho phép
chương trình viết ra chạy trong chế độ bảo vệ, có thể sử dụng được vùng nhớ mở rộng (có
trên các máy 286 trở lên), Chế độ Windows cho phép chương trình có thể chạy như một
ứng dụng của hệ điều hành Windows. Lập trình trong chế độ bảo vệ của BP7 rất phức tạp
và có thể nói là một sự đối phó, để lập trình trên Windows phải cần các công cụ chuyên
dụng hơn. Ngày nay, khi làm các bài thi bằng BP7, người ta bắt buộc viết trong chế độ
thực.
• P rimary file: file ưu tiên hàng đầu, nếu đặt Primary file thì khi chạy chương trình nó sẽ
chạy file này bất kể cửa sổ hoạt động chứa file nào, điều này hữu ích khi ta đang viết một
thư viện (UNIT) và cần có một program file để thử các thủ tục và hàm trong thư viện
(Unit không chạy trực tiếp được như Program).
• Clear primary file: Xoá primary file, không phải là xoá trên đĩa mà chỉ là vô hiệu hoá việc
đặt primary file mà thôi
• I nformation: Cho biết thông tin biên dịch, số dòng, kích thước mã lệnh, kích thước bộ nhớ
thi hành v.v
Đối với Turbo Pascal
Menu Compiler của TP7 gần giống như BP7, chỉ khác là không có menu Target, mà thay vào
đó là menu Destination. Turbo Pascal không lập trình trực tiếp được trong chế độ nào khác
ngoài chế độ thực. Menu Destination cho người sử dụng hai lựa chọn: Disk và Memory, nếu

đặt là Disk thì khi dịch chương trình, Turbo Pascal sẽ dịch thành một file .EXE trên đĩa và file
6
đó có thể chạy trực tiếp từ dấu nhắc DOS. Nếu đặt là Memory thì mã biên dịch sẽ được đặt
trực tiếp vào bộ nhớ và thi hành ngay trong TP7. Lưu ý rằng BP7 bỏ đi menu này vì nó luôn
luôn dịch ra đĩa và tạo ra file .EXE.
II.6. D ebug menu (Alt + D): Các chức năng gỡ rối
• B reakpoints Điều khiển điểm dừng chương trình.
• C all stack: Khi chương trình dừng lại, muốn kiểm tra xem khi chạy tới điểm dừng thì thứ
tự các chương trình con gọi xếp chồng và tham số của nó như thế nào thì chọn chức năng
này
• R egister: Mở cửa sổ theo dõi giá trị các thanh ghi của 8088, hữu ích nếu lập trình có chèn
nhiều mã lệnh Assembly
• W atch: Mở cửa sổ Watches để theo dõi giá trị của biến trong khi gỡ rối
• O utput: Mở cửa sổ theo dõi kết quả ra màn hình ra sao, ít công dụng, người ta hay dùng
User screen hơn.
• U ser screen: Tạm ẩn màn hình BP7, cho hiện màn hình kết quả ra, điều này rất có ý nghĩa
bởi các chương trình viết và chạy trong BP7, khi chạy xong sẽ tự động quay về màn hình
của BP7 nên không kịp nhìn kết quả. Muốn trở lại màn hình BP7 thì gõ một phím bất kỳ.
• E valuate/ modify: gõ tên biến hoặc biểu thức sẽ cho kết quả, dùng làm máy tính tay rất tốt
bởi nó tính được cả biểu thức có biến và xử lý được cả dấu ngoặc. Hơn nữa, nó cho phép
thay đổi giá trị biến trong quá trình chạy
• A dd watch Thêm một biến, biểu thức vào cửa sổ Watches để theo dõi giá trị trong quá
trình gỡ rối.
• Add breakpoint: Tạo điểm dừng lúc chạy, để xem giá trị một biến lúc dừng là bao nhiêu
chẳng hạn
II.7. T ools menu (Alt + T): Các công cụ hỗ trợ
7
Các công cụ thêm cho BP7, có thể do chính bản hãng viết hoặc hãng khác viết, nếu có điều
kiện tham khảo thêm trong phần Help. Bạn có thể viết thêm các công cụ khác bổ sung cho
Menu này: Ví dụ: Công cụ hiện bảng mã ASCII, công cụ đo thời gian, v.v

II.8. O ptions menu (Alt + O): Đặt các tham số tuỳ chọn
• C ompiler : Đặt các dẫn hướng biên dịch, có kiểm tra tràn phạm vi hay không ?, có kiểm
tra lỗi vào ra đĩa hay không ?
• M emory sizes Đặt lại kích thước bộ nhớ dành cho chương trình gồm có bộ nhớ Stack
dành cho các biến địa phương và mã lệnh chương trình con, bộ nhớ Heap dành cho các
biến cấp phát động
• L inker : Trình biên dịch khi dịch một chương trình, nó phải dịch một số thư viện mà
chương trình dùng đến sau đó liên kết và mã lệnh chương trình, chức năng này đặt một số
tuỳ chọn cho quá trình liên kết.
• Debugger : Đặt một số tuỳ chọn cho trình gỡ rối
• D irectories : Đặt đường dẫn tìm file, trong đó có việc đặt file EXE khi dịch ra vào thư
mục nào, tìm các thư viện ở đâu v.v
• T ools: Thêm công cụ hỗ trợ cho lập trình viên vào menu Tools, thường là một file EXE ví
dụ như chương trình hiện bảng mã ASCII chẳng hạn.
• E nvironment: Đặt các tham số môi trường phát triển chẳng hạn như đặt màu chữ, màu nền
trong cửa sổ soạn thảo, đặt chế độ khi chạy chương trình thì tự động ghi file v.v
• O pen, Save, và Save as: Mở file chứa các tuỳ chọn/ ghi các tuỳ chọn vào file (.TP)
8
II.9. W indow menu(Alt+W) : Quản lý cửa sổ
• T ile: Sắp xếp các cửa sổ trên màn hình BP7 bằng cách "lát" kín màn hình BP7. Ví dụ như
có 4 cửa sổ đang mở thì mỗi cửa sổ lúc đó sẽ chiếm 1/4 màn hình
• Cascade: Sắp xếp các cửa sổ trên màn hình BP7 theo cách xếp chồng
• Close all: Đóng tất cả các cửa sổ
• R efresh display: Lập trình truy cập trực tiếp bộ nhớ màn hình khiến cho màn hình BP7 đôi
khi gặp "rác" thì dùng chức năng này để quét hết rác. Nếu chỉ dùng các lệnh vào ra thông
thường thì chẳng bao giờ dùng chức năng này
• S ize/ Move: Thay đổi kích thước, vị trí cửa sổ. Bấm Ctrl+F5 sau đó dùng các phím di
chuyển ←↑→↓ để dịch chuyển cửa sổ, dùng Ctrl kết hợp với các phím: ← để thu nhỏ
chiều ngang; ↑ để thu nhỏ chiều dọc; → để nới rộng chiều ngang, ↓ để nới rộng chiều dọc
• Z oom: Phóng to cực đại cửa sổ hoạt động

• N ext: Chuyển cửa sổ hoạt động sang cửa sổ kế sau (có thể bấm Alt + số hiệu cửa sổ)
• P revious: Chuyển cửa sổ hoạt động sang cửa sổ kế trước (có thể bấm Alt + số hiệu cửa sổ)
• C lose: Đóng cửa sổ hoạt động.
• L ist: Hiện danh sách các cửa sổ.
II.10. H elp menu (Alt + H): Trợ giúp
9
• C ontents: Toàn bộ nội dung trợ giúp, nếu không tính đến các cấu trúc bí mật của BP7 thì
đây là cuốn sách tra cứu đủ nhất trong tất cả mọi cuốn sách về BP7.
• I ndex: Tìm trợ giúp theo chỉ mục, giống như tra từ điển. Ví dụ muốn xem thủ tục Val viết
thế nào thì vào chỉ mục V
• T opic search: Di chuyển con trỏ tới một từ nào đó trong văn bản, từ khoá, thủ tục, hàm
chuẩn và bấm Ctrl + F1 sẽ hiện ra các thông tin liên quan.
• P revious topic: Quay lại chỉ mục trước đây vừa tra.
• Using help: Hướng dẫn dùng sách hướng dẫn
• F iles: Các file trợ giúp
• Compiler directives: Hướng dẫn sử dụng các cách thiết lập dẫn hướng biên dịch
• procedures and functions: Danh mục các thủ tục và hàm đã được BP cài đặt sẵn.
• R eserved words: Danh mục các từ khoá
• Standard units: Các thư viện chuẩn (Crt, Graph, Dos )
• Borland Pascal Language: Ngôn ngữ Pascal chuẩn quá cứng nhắc đối với lập trình viên,
BP7 đã mở rộng ngôn ngữ Pascal để có được sự mềm dẻo hơn, mục này trình bày những
sự sửa đổi đó.
• E rror messages: Các thông báo lỗi và cách xử lý
• A bout : Trình bày giới thiệu BP7, tên hãng sản xuất và các vấn đề bản quyền.
Trên đây là trình bày sơ qua về hệ thống menu của BP7, có thể nói là có rất nhiều chức năng
và khó nhớ. Nên đặt yêu cầu là trừ những chức năng thường xuyên dùng nhất thiết phải nhớ,
các chức năng còn lại ta nhớ được càng nhiều càng tốt và học thêm dần dần.
III. LẬP TRÌNH CẤU TRÚC TRÊN BP7
Lập trình là một công việc khó đòi hỏi phải có tư duy nhanh nhạy và sự tuân thủ kỷ luật
nghiêm ngặt trong phong cách lập trình. Tư duy tốt để có thể nắm bắt được cấu trúc bài toán

và tìm ra cách giải, còn phong cách tốt là để biến tất cả những cố gắng của trí não và đôi tay
thành kết quả cuối cùng. Một điều rất hay thấy ở người mới học lập trình là có thể họ nắm bắt
được thuật toán rất nhanh nhưng do cách làm việc thiếu hiệu quả nên tiêu tốn rất nhiều thời
gian thậm chí càng làm càng bị rối trong hết lỗi này đến lỗi khác. Như vậy một phong cách
lập trình tốt trước hết phải là một phong cách viết chương trình có xác suất xảy ra lỗi thấp
nhất, và cho dù có xảy ra lỗi thì cũng dễ dàng chỉnh sửa. Còn việc viết các chương trình cỡ
trung bình và lớn mà không có lỗi thì trừ khi sau này người ta sáng tạo ra các máy lập trình
chứ nhân loại thì chẳng có ai viết chương trình máy tính mà không có lỗi cả.
Ta sẽ phân tích hai vấn đề: "Làm thế nào để hạn chế lỗi" và "nếu có lỗi thì tìm và sửa thế
nào".
Hạn chế lỗi thường được quyết định bởi hai yếu tố: Môi trường lập trình và phong cách lập
trình. Cách đây vài chục năm với một máy tính cỡ lớn dùng băng đục lỗ, thì rõ ràng khả năng
hạn chế và tìm lỗi là rất ít bởi mọi lỗi đều dẫn tới kết quả sai hoặc treo máy, hay với một ngôn
ngữ lập trình cấp thấp thì ta chỉ có thể viết những đơn vị chương trình nhỏ mà thôi, bởi tính
phi cấu trúc của nó làm cho số lỗi ước tính sẽ tăng theo bình phương chiều dài chương trình.
Như vậy, xét cho cùng, phải phát triển các ngôn ngữ lập trình (ADA, PASCAL, C++, BASIC)
và chương trình dịch (TP, Borland C, Delphi, VB), phải sáng tạo ra các cách thiết kế (Từ trên
xuống, hướng sự kiện, hướng đối tượng) chính là để giảm bớt công sức cho các lập trình
10
viên trong việc sửa lỗi để có thể tạo ra những sản phẩm lớn hơn, hoàn thiện hơn, ít lỗi hơn.
Rất may cho chúng ta là được làm việc với thế hệ máy tính nhanh, ổn định và thân thiện như
ngày nay và với một trình biên dịch như BP7 có khả năng bắt lỗi rất tốt và hỗ trợ lập trình cấu
trúc.
Phong cách lập trình cấu trúc
3
trên BP7 nên thế nào để hạn chế lỗi?
• Viết một chương trình trên BP7 phải tuân theo các bước: Tìm hiểu yêu cầu bài toán → tìm
thuật toán → xây dựng cấu trúc dữ liệu và dựng giải thuật → Viết chương trình + Thử +
Sửa lỗi → Nâng cấp → Hoàn thành. Không được đảo lộn hay bỏ qua bước nào. Lưu ý
riêng bước viết chương trình + thử + sửa lỗi, không phải ta viết xong cả chương trình rồi

mới thử mà khi có bất kỳ cơ hội nào để thử chạy chương trình (dù là chỉ xong một phần
nhỏ) ta chạy thử ngay để có thể tạm yên tâm rằng phần vừa viết không có lỗi.
• Viết chương trình theo đúng thiết kế từ trên xuống, đầu tiên viết chương trình chính
trước, gồm các bước rất tổng quát, tiếp theo cụ thể hoá từng bước bằng cách viết các
chương trình con tương ứng, thậm chí đôi khi tại mỗi bước cụ thể hoá ta chỉ làm đến mức
độ nào đó rồi lại viết tiếp các chương trình con v.v cách làm này dựa trên nguyên tắc
"chia để trị" rất hiệu quả và đã được thực tế chứng minh. Có một vài ý kiến còn cho rằng,
để tiện quan sát, tất cả các chương trình con viết theo cách trên không nên có độ dài quá
một trang màn hình. Một số người có thói quen viết chương trình cứ từ đầu chí cuối, làm
hết chương trình con rồi đến chương trình chính. Cách làm như vậy không logic một chút
nào bởi khi chưa viết ra chương trình chính, ta đâu đã xác định được nhiệm vụ của các
chương trình con, lại càng không xác định được là phải truyền cho chương trình con bao
nhiêu tham số và là những tham số nào. Đối với chương trình nhỏ thì có thể không nhận
thấy điều đó nhưng với những chương trình lớn thì cách làm như trên sẽ phải trả giá rất
đắt bằng rất nhiều động tác xoá đi viết lại.
• Viết chương trình thì khó tránh phải lỗi cú pháp (Syntax), thiếu dấu thiếu chữ trong việc
soạn chương trình cũng là thường tình. Các lỗi thiếu ";", thiếu ngoặc, thiếu ":", tên chưa
khai báo, v.v thì chỉ cần trình biên dịch báo lỗi đúng chỗ đó là sửa được ngay. Cái lỗi cú
pháp sửa mất nhiều thời gian nhất là lỗi sai cấu trúc khối (Có "begin", thiếu "end;" hay có
"repeat" thiếu "until" v.v ). Trình biên dịch không thể chỉ ra được chỗ thiếu bởi ví dụ
như có "begin" mà không có "end;" thì chữ "end;" thiếu có thể cho ở nhiều nơi khác nhau
mà vẫn hợp lý cả. Trình dịch chỉ thông báo lỗi chung chung là thiếu ";" mà thôi và như
vậy thời gian của chúng ta lại lãng phí vào việc dò trong cả chương trình xem thiếu chỗ
nào. Trong khi lỗi đó sẽ không bao giờ xảy ra nếu như khi soạn chương trình, mỗi khi gõ
xong phần mở khối thì ta gõ luôn phần kết khối và lùi vào gõ đoạn giữa khối sau. Điều đó
không những làm cho khối lệnh được sáng sủa, mở khối kết khối thẳng hàng mà còn làm
ta không phải bận tâm gì về lỗi thiếu cấu trúc khối nữa.
begin



end;
case M of


end;
repeat


until ;
R = record


end;
C = object(P)


end;
asm


end;
• Đặt tên gợi nhớ chức năng, không ngại đặt tên dài, trước khi dùng biến phải cân nhắc xem
tầm hoạt động của nó là địa phương hay toàn cục, nhiệm vụ của nó để làm gì. Nên đặt tên
tiếng Anh bởi nếu đặt tiếng Việt khi viết vào máy tính không có dấu dễ bị hiểu theo nghĩa
3
Đây chỉ nói đến lập trình cấu trúc, lập trình hướng đối tượng có phong cách khác
11
khác. Còn một số việc nhỏ nữa tuy không quan trọng lắm: Từ khoá viết thường, tên thủ
tục và hàm chuẩn viết hoa đầu từ tiếng Anh. Ví dụ: not, xor, and, or, mod, div, SizeOf,
LongInt, Integer, Abs, Sqrt, Sin, Cos, v.v Các tên định nghĩa trong chương trình khi khai

báo viết hoa, thường thế nào thì thống nhất trong cả chương trình viết hoa, thường như
thế. Sau dấu phân cách từ (phẩy hoặc chấm, chấm phẩy ) nên có 1 dấu cách, trước và sau
dấu gán := đều có dấu cách. Đọc thêm các chương trình mẫu của Borland để rõ điều này.
• Hạn chế tối đa việc viết các thủ tục và hàm lồng nhau quá nhiều cấp, gây rối trong việc
đọc chương trình.
Nếu ta viết chương trình mà bị lỗi thì sửa như thế nào?
Như đã nói ở trên, ta nói sửa lỗi ở đây là sửa lỗi sai trong cài đặt thuật toán chứ không nói đến
lỗi syntax nữa. Muốn sửa lỗi cài đặt giải thuật thì về cơ bản các kỹ thuật gỡ rối là khoanh
vùng xác định lỗi tức là thu hẹp phạm vi dò tìm tới khi xác định chính xác lỗi và sửa.
Lỗi chương trình xảy ra khi ta có một bộ dữ liệu cho vào chương trình chạy được kết quả
không theo ý muốn. Khi đó ta sử dụng trình gỡ rối Debugger của BP7 như sau:
Bước 1: Mở cửa số Watches theo dõi giá trị bằng cách bấm Ctrl + F7 hay chọn menu
Debug/Add Watch. Gõ tên biến hay biểu thức cần theo dõi, tên biến sẽ hiện ra trong cửa sổ
Watches. Dùng phím F6 để luân chuyển hoạt động giữa cửa sổ soạn thảo và cửa sổ Watches.
Bấm Ctrl + F5 hay chọn menu Window/Size/Move sau đó điều chỉnh vị trí và kích thước hai
cửa sổ sao cho phù hợp nhất, tốt nhất không nên che nhau.
Bước 2: Bấm F8 (Run/ Step over) để thực hiện chương trình từng bước, khi đó trên cửa sổ
soạn thảo sẽ có một vạch ngang cho biết chương trình đã chạy tới dòng nào. Mỗi lần bấm F8
thì chương trình chạy đúng 1 dòng và dừng lại, thông báo giá trị các biến được theo dõi trong
cửa số Watches. Nhớ rằng mỗi lần chạy qua 1 bước, ta phải tự tính xem, nếu đúng thì giá trị
biến theo dõi phải là bao nhiêu và so sánh với kết quả trong cửa sổ Watches, nếu giống
12
thì dò tiếp, nếu sai thì chắc chắn dòng lệnh vừa chạy qua là dòng lệnh gây lỗi, ta đã khoanh
vùng được một lần.
Bước 3: Nếu dòng lệnh gây lỗi chỉ là một dòng lệnh tương đối đơn giản: như biểu thức số học
chẳng hạn thì nhiều khả năng do ta gõ sai hoặc gõ thừa thiếu một yếu tố gì đó, ta xem kỹ lại
biểu thức đó và sửa. Nhưng nếu dòng lệnh gây lỗi lại là lời gọi chương trình con thì sao. Ta
dừng việc gỡ rối bằng cách bấm Ctrl + F2 (Run/ Program reset) và bắt đầu lại từ đầu, chỉ có
điều khi đến dòng lệnh gây lỗi ta không bấm F8 chạy qua nữa mà ấn F7 (Run/ Trace into) để
trình gỡ rối truy vết vào chương trình con và lại thực hiện chạy từng bước (Step over - F8)

hay truy vết (Trace into - F7) tiếp tục thu hẹp phạm vi tìm lỗi.
Lưu ý:
1. Cửa sổ Watches có thể theo dõi cùng lúc nhiều biến
2. Để tiết kiệm thao tác cho đỡ phải bấm F8 quá nhiều ta có thể di chuyển con trỏ tới một
dòng và bấm F4 (Run/ Go to cursor) để cho chương trình sẽ chạy tới dòng đó thì dừng lại,
hiện ra vạch ngang và từ đó gỡ rối tiếp.
3. Khi gỡ rối ta phải dùng khá nhiều cửa sổ, chính vì vậy mà những cửa sổ nào không cần
nhất thiết nên đóng lại để khỏi tốn bộ nhớ và che lấp các cửa sổ có ích.
4. Ta cần phải kết hợp cả kỹ thuật đưa giá trị trung gian ra màn hình và tạo điểm dừng theo
điều kiện để tiện gỡ rối vòng lặp, ví dụ:
Xét đoạn chương trình
for i := 1 to 1000 do
for j := 1 to 1000 do
begin
S1;
S2;
end;
Giả sử ta biết được lệnh S1 là lệnh gây lỗi, nhưng không phải lúc nào cũng lỗi, nó chỉ gây
lỗi khi i = 501 và j =1000 (điều này có thể biết được bằng cách đưa giá trị trung gian của i
và j ra màn hình). Nếu ta sử dụng lệnh chạy từng bước F8 thì không ổn bởi như vậy ta sẽ
phải bấm 500x1000 lần. Khi đó ta làm như sau, di chuyển con trỏ đến dòng chứa lệnh S1.
Chọn Debug/ Add breakpoint. Gõ vào ô Condition điều kiện: (i = 501) and (j = 1000) sau
đó bấm Enter để máy nhận. Cuối cùng chỉ việc bấm Ctrl+F9 để chạy, khi chạy đến dòng
chứa lệnh S1 mà có i = 501 và j = 1000 thì máy sẽ dừng và hiện vạch ngang cho gỡ rối
tiếp (gỡ theo vết vào chương trình con S1 chẳng hạn).
5. Cuối cùng, rất quan trọng trong gỡ rối là phải xác định chính xác lỗi, đi sửa lung tung
theo kiểu "có bệnh vái bốn phương" là điều tối kỵ, bởi sửa không đúng chỗ thì lỗi ngày
càng tai hại.
IV. CÁC DẪN HƯỚNG BIÊN DỊCH
Dẫn hướng biên dịch (Compiler Directive) là đặt chế độ bắt buộc trình biên dịch sinh mã theo

một điều kiện nào đó. Để đặt dẫn hướng biên dịch ta có cú pháp
• {$<Dẫn hướng biên dịch>} hoặc
• (*$<Dẫn hướng biên dịch>*)
Hoặc ta mở menu Options/Compiler và đánh dấu vào các ô trong hộp thoại:
13
Sau đây là một số dẫn hướng biên dịch hay sử dụng nhất.
IV.1. Kiểm tra tràn phạm vi (Range Checking)
• Cú pháp: {$R-} và {$R+}
• Công dụng: Cho phép ({$R+}) hay cấm ({$R-}) trình biên dịch chèn thêm các mã kiểm
tra phạm vi.
• Tầm hoạt động: tất cả các dòng lệnh viết sau dẫn hướng này đều bị ảnh hưởng
• Lệnh trên thanh menu tương ứng: Options/Compiler/Range checking
• Mặc định (Default): Nếu không dẫn hướng gì thì BP7 hiểu là {$R-}
Giải thích:
Trong chế độ biên dịch {$R+}, tất cả các thao tác trên mảng và xâu ký tự đều được kiểm tra
có bị truy cập ra ngoài mảng hay xâu ký tự hay không, tất cả các thao tác trên các biến kiểu vô
hướng, liệt kê, và khoảng con đều được kiểm tra xem có bị gán giá trị ngoài vùng hay không.
Nếu có thao tác truy cập ngoài phạm vi, chương trình sẽ dừng ngay và thông báo lỗi Runtime
error 201. Nếu còn chạy trong BP7 thì con trỏ sẽ đứng tại dòng gây lỗi và trên màn hình soạn
thảo sẽ có thông báo:
Error 201: Range check error
Trong chế độ biên dịch {$R-} thì thao tác truy cập ngoài phạm vi không được kiểm tra, có thể
làm hỏng giá trị biến khác hoặc truy cập vào vùng nhớ cấm gây treo máy.
Ví dụ: Xét hai đoạn chương trình sau: Hai chương trình chỉ khác nhau ở dẫn hướng biên dịch
Disable Range checking Enable Range checking
{$R-}
program Test1;
var
i: Integer;
a: array[1 10] of Integer;

n: Integer;
begin
n := 10;
{$R+}
program Test2;
var
i: Integer;
a: array[1 10] of Integer;
n: Integer;
begin
n := 10;
14
Disable Range checking Enable Range checking
i := 11;
a[i] := 1;
Writeln(n);
end.
i := 11;
a[i] := 1;
Writeln(n);
end.
Chương trình Test1 sẽ cho ra kết quả số n là 1, lý do: lệnh gán a[i] := 1 ở trên là thao tác ngoài
phạm vi, i = 11 mà mảng a thì [1 10], phép gán đó sẽ làm hỏng giá trị biến n đã được gán là
10 từ trước.
Chương trình Test2 thì không như vậy, máy sẽ dừng lại ở dòng lệnh gán a[i] := 1 và thông
báo lỗi.
Kinh nghiệm: Khi dẫn hướng {$R-}, truy cập ngoài phạm vi tất nhiên là lỗi, nhưng nhiều khi
nó vẫn không làm chương trình chạy sai bởi có thể nó chỉ làm hỏng giá trị một biến không
dùng tới, hoặc điền giá trị vào một vùng nhớ vô hại. Điều này rất nguy hiểm bởi khi đó
chương trình sẽ chạy với bộ dữ liệu này đúng, dữ liệu kia sai. Nhưng nếu đặt dẫn hướng

{$R+} thì chương trình sẽ phải thêm công thêm việc dẫn tới chậm hơn, lớn hơn. Tốt nhất nên
đặt chế độ {$R+} khi chạy thử để sửa hết lỗi phạm vi, sau đó đặt lại {$R-}
IV.2. Kiểm tra tràn ngăn xếp chương trình con.
Ngăn xếp dành cho chương trình con là một vùng nhớ đặc biệt dùng để chứa các biến địa
phương của các chương trình con, người ta dùng thuật ngữ Stack để gọi tên vùng nhớ này.
• Cú pháp: {$S-} và {$S+}
• Công dụng: Cho phép ({$S+}) hoặc cấm ({$S-}) trình biên dịch sinh mã kiểm tra tràn
ngăn xếp
• Tầm hoạt động: tất cả các dòng lệnh viết sau dẫn hướng này đều bị ảnh hưởng
• Lệnh trên thanh menu tương ứng: Options/Compiler/Stack checking
• Mặc định (Default): Nếu không dẫn hướng gì thì BP7 hiểu là {$S+}
Giải thích:
Ở chế độ {$S+}, nếu gọi một chương trình con mà không còn đủ bộ nhớ dành để lưu các biến
địa phương và mã lệnh của nó thì chương trình sẽ thông báo lỗi Runtime error 202. Nếu còn
chạy trong BP7 thì sẽ có thông báo lỗi:
Error 202: Stack overflow error
Ví dụ: Hai chương trình chỉ khác nhau ở dẫn hướng biên dịch
Disable Stack checking Enable Stack checking
{$S-}
program StackTest1;
procedure Proc(i: LongInt);
begin
Write(i:20);
Proc(i + 1);
end;
begin
Proc(1);
end.
{$S+}
program StackTest2;

procedure Proc(i: LongInt);
begin
Write(i:20);
Proc(i + 1);
end;
begin
Proc(1);
end.
15
Chương trình 1, máy sẽ chạy mãi mà không báo lỗi gì. Chương trình 2 khi chạy tới i = 1983
sẽ bị lỗi
Kinh nghiệm: {$S-} rất nguy hiểm, nên dùng {$S+} trong khi thử để sửa hết lỗi tràn ngăn
xếp, sau đó đặt lại {$S-} để chương trình chạy nhanh và bé hơn. Trong trường hợp chương
trình có đệ quy hay dùng nhiều biến địa phương, nên đặt kích thước Stack lên tối đa bằng dẫn
hướng biên dịch
{$M 65520,0,655360}
IV.3. Kiểm tra vào/ra dữ liệu
• Cú pháp: {$I-} và {$I+}
• Cho phép ({$I+}) hoặc cấm ({$I-}) trình biên dịch sinh mã kiểm tra vào/ra dữ liệu
• Tầm hoạt động: tất cả các dòng lệnh viết sau dẫn hướng này đều bị ảnh hưởng
• Lệnh trên thanh menu tương ứng: Options/Compiler/I/O checking
• Mặc định (Default): Nếu không dẫn hướng gì thì BP7 hiểu là {$I+}
Giải thích:
Ở chế độ {$I+} bất kỳ một lỗi vào/ ra dữ liệu nào đều làm dừng chương trình và hiện thông
báo lỗi, {$I-} thì không.
Kinh nghiệm: Nếu trong chế độ {$I-}, sau mỗi thao tác vào/ ra dữ liệu ta có thể dùng hàm
IOResult để kiểm tra xem có lỗi hay không, nếu hàm cho kết quả là 0 tức là không có lỗi. Lưu
ý rằng gọi hàm IOResult hai lần sau một thao tác vào ra dữ liệu thì lần gọi 2 luôn cho kết quả
0.
Trong chế độ {$I-} nếu là lệnh nhập một số nguyên mà ta gõ vào một chữ cái thì sẽ không bị

dừng chương trình và báo lỗi, ta có thể cho nhập lại: Ví dụ:
{$I-}
program IO_Test;
var
i: Byte;
begin
repeat
Readln(i);
until IOResult = 0;

end.
IV.4. Kiểm tra lỗi tràn số học
• Cú pháp: {$Q-} và {$Q+}
• Cho phép ({$Q+}) hoặc cấm ({$Q-}) trình biên dịch sinh mã kiểm tra tràn phép tính số
học.
• Tầm hoạt động: tất cả các dòng lệnh viết sau dẫn hướng này đều bị ảnh hưởng
• Lệnh trên thanh menu tương ứng: Options/Compiler/Overflow checking
• Mặc định (Default): Nếu không dẫn hướng gì thì BP7 hiểu là {$Q-}
Giải thích:
Trong chế độ {$Q+}, tất cả các biểu thức số nguyên đều được kiểm tra lỗi tràn, nếu gặp lỗi thì
chương trình sẽ dừng ngay và hiện thông báo lỗi Runtime error 215. Nếu đang ở trong BP thì
sẽ có thông báo lỗi:
16
Error 215: Arithmetic overflow.
Trong chế độ {$Q+}, thao tác tính biểu thức số học không được kiểm tra, tuy không nguy hại
tới mức treo máy hay phá huỷ dữ liệu nhưng sẽ làm biểu thức bị tính sai.
Ví dụ: Xét hai chương trình sau, hai chương trình chỉ khác nhau ở dẫn hướng biên dịch.
Disable Overflow checking Enable Overflow checking
{$Q-}
program OverflowTest1;

var
a, b: Integer;
c: LongInt;
begin
a := 30000; b := 10000;
c := a + b;
Writeln(c);
end.
{$Q+}
program OverflowTest2;
var
a, b: Integer;
c: LongInt;
begin
a := 30000; b := 10000;
c := a + b;
Writeln(c);
end.
Chương trình 1 sẽ cho kết quả c = -25536 (sai). Chương trình 2 sẽ dừng và báo lỗi tràn số học.
Kinh nghiệm:
a) Vấn đề ép kiểu trong BP7.
Nguyên lý tính toán một biểu thức số học trong BP là:
• Tính nhân và chia trước, cộng và trừ sau. (Nếu có ngoặc thì tính biểu thức trong ngoặc
trước)
• Nếu như các phép toán R toàn là phép cộng hay phép trừ, hoặc toàn là phép nhân và phép
chia (div, mod) thì phép tính: a R b R c sẽ được tính bằng cách lấy a R b trước, rồi đem
kết quả đó R với c.
Giả sử ta tính biểu thức a R b (R là một trong các phép toán +, -, *, div, mod; a và b là các số
nguyên).
• Trước hết, nếu a và b là biến Byte hay ShortInt thì BP coi như a và b là các biến Integer.

• Sau đó biểu thức sẽ được tính kết quả dưới dạng một kiểu số nguyên vừa đủ nhỏ để bao
trùm kiểu dữ liệu của a và b.
Ví dụ:
• Integer + Integer = Integer
• Integer * Byte = Integer
• Integer + Word = LongInt
• Word + Word = Word
• Byte * Byte = Integer
• Byte div ShortInt = Integer
• LongInt mod LongInt = LongInt.
Do nguyên lý phép gán, chương trình sẽ tính biểu thức vế trái phép gán trước, được bao nhiêu
mới đem ép kiểu trùng với về phải trước khi gán cho vế phải, vậy nên khi tính c := a + b ở
trên, do a và b là Integer nên phép tính a + b = 40000 sẽ tràn phạm vi Integer, cho dù c có
mang kiểu số thực chăng nữa thì cũng vậy thôi. Để phép tính đó không bị tràn, ta ép trình dịch
của BP hiểu a là biến LongInt (hoặc Word): c := LongInt(a) + b
Phép cộng bây giờ không còn là Integer + Integer = Integer nữa mà là LongInt + Integer =
LongInt.
17
Xem xét chương trình sau:
{$Q+}
program OverflowTest;
var
a, b, c: Integer;
d: LongInt;
begin
a := 30000; b := 10000; c := 10000;
d := a + b + LongInt(c);
Writeln(d);
end.
Vế phải lệnh gán giá trị cho d là: Integer + Integer + LongInt, nhưng khi chạy vẫn bị lỗi tràn

số. Bởi để tính a + b + LongInt(c), máy sẽ tính a + b trước. Phép ép kiểu đúng phải là:
LongInt(a) + b + c. Chính vì vậy để tránh lỗi tràn số, ta phải nhìn vào từng phép tính trong
biểu thức và ép kiểu đúng chỗ, đặc biệt là biểu thức có lẫn cộng và nhân.
Có ý kiến cho rằng để an toàn, bất kể kiểu dữ liệu nguyên bài toán cần thế nào, ta đều khai
LongInt, nhưng làm như vậy sẽ làm chương trình chậm đi và tốn bộ nhớ, chẳng hạn ta không
thể khai báo một mảng 20000 phần tử kiểu LongInt được. Ép kiểu là một kỹ thuật cao cấp,
nếu tận dụng tốt sẽ phát huy được cả tốc độ của chương trình và khả năng an toàn khi tính
toán biểu thức.
b) Khi nào không nên dùng Byte và ShortInt ?.
Khi khai báo một biến đơn, (không phải biến mảng). Tốt nhất không nên dùng kiểu Byte
và ShortInt. Khai báo như vậy chẳng tiết kiệm bộ nhớ bao nhiêu, lại làm chương trình chậm
hơn chút ít, mà ta luôn luôn phải đề phòng các phép gán và phép nhân bị tràn số, tạo tâm lý ức
chế khi lập trình. Bởi kiểu Byte bị ép thành Integer trong biểu thức số học nên phép nhân hai
biến kiểu Byte bằng 200 sẽ bị tràn số. Tệ hơn nữa, BP không phát hiện ra lỗi tràn số này kể cả
trong {$Q+}, chỉ cho kết quả sai mà thôi, điều đó quả thực hết sức nguy hiểm bởi trong thời
gian hạn hẹp, ta không có đủ tỉnh táo để kiểm tra đủ hết mọi trường hợp. Ta có thể vẫn thấy
đâu đó các chương trình mẫu sử dụng các biến đơn kiểu Byte và ShortInt, nhưng phải nói
rằng những chương trình đó đã được lập trình và kiểm tra rất cẩn thận trong thời gian có thể
nói là không hạn chế.
Ta cũng không nên dùng mảng các phần tử kiểu Byte trong trường hợp đủ không gian lưu trữ
và vấn đề không bị phức tạp thêm nếu dùng Integer.
IV.5. Thực hiện đầy đủ biểu thức Logic.
• Cú pháp: {$B-} và {$B+}
• Công dụng: Cho phép (B+) hoặc cấm (B-) trình biên dịch sinh mã hoàn thành đủ biểu thức
logic
• Tầm hoạt động: tất cả các dòng lệnh viết sau dẫn hướng này đều bị ảnh hưởng
• Lệnh trên thanh menu tương ứng: Options/Compiler/Complete boolean eval
• Mặc định (Default): Nếu không dẫn hướng gì thì BP7 hiểu là {$B-}
Giải thích:
Ở chế độ {B+} việc kiểm tra một biểu thức logic là TRUE hay FALSE sẽ được tính đầy đủ, ở

chế độ {$B-} nếu tính một phần biểu thức mà đã xác định được giá trị biểu thức thì sẽ không
tính tiếp nữa.
18
Ví dụ: Hai chương trình chỉ khác nhau ở dẫn hướng biên dịch:
{$B-}
program BoolTest1;
var
i: Integer;
begin
i := -1;
if (1 + 1 = 2) or
(Sqrt(i) = 9) then
Writeln(i);
end.
{$B+}
program BoolTest2;
var
i: Integer;
begin
i := -1;
if (1 + 1 = 2) or
(Sqrt(i) = 9) then
Writeln(i);
end.
Trong chương trình 1, khi đến lệnh if máy sẽ tính biểu thức điều kiện. Vì 1+1=2 chắc chắn
mang giá trị TRUE nên bất kể biểu thức sau đúng sai thế nào điều kiện cũng đúng, máy sẽ
không cần tính tiếp biểu thức sau nữa mà thực hiện lệnh in kết quả ngay (giá trị i = -1). Trong
chương trình 2, vì có dẫn hướng {$B+} nên máy sẽ tính đủ biểu thức điều kiện và nó sẽ phải
tính Sqrt(i), việc này gây lỗi vì i đang có giá trị âm (-1).
(Trên đây là ví dụ phép or với giá trị TRUE, vấn đề tương tự đối với phép and và giá trị

FALSE)
Kinh nghiệm:
Xét một ví dụ rất đơn giản: Cho dãy a gồm 10 phần tử chỉ gồm 0 và 1. Tìm số 0 đầu tiên từ
cuối dãy (số 0 cuối cùng trong dãy), nếu thấy thay bằng số 1. Chương trình có kiểm tra lỗi
phạm vi, không thực hiện đủ biểu thức logic:
{$R+,B-}
program BoolTest;
var
a: array[1 10] of Byte;
i: Byte;
begin
for i := 1 to 10 do Readln(a[i]);
i := 10;
while (i > 0) and (a[i] = 1) do Dec(i);
if i > 0 then a[i] := 1;
end.
Với bộ dữ liệu toàn số 1, chương trình trên hoạt động bình thường với dẫn hướng {$B-}
nhưng sẽ gặp lỗi tràn phạm vi khi dẫn hướng {$B+}. Bởi khi đặt i = 10 và lùi dần i đến tận i =
0 (bởi vì dãy toàn số 1) thì khi kiểm tra điều kiện while do, máy sẽ phải tính cả điều kiện
(a[i] = 1) đây là lệnh gây tràn phạm vi bởi i = 0 mà a là [1 10]. Như vậy, trong đa số trường
hợp, ta nên đặt chế độ biên dịch {$B-}, trừ phi vì một lý do nào đó (bắt buộc phải thực hiện
hàm chẳng hạn) ta phải cho tính đầy đủ biểu thức logic.
IV.6. Bộ đồng xử lý toán học 80x87
Bộ xử lý CPU 80x86 được thiết kế để tính toán trên các số nguyên rất tốt, đặc biệt là các số
nguyên 16 bít (Integer, Word), nhưng lại không được thiết kế cho việc tính toán số thực. Các
hàm tính toán số thực Real của PASCAL vẫn tính được là nhờ giả lập các phép toán trên số
thực, nó tính toán các hàm sin, cos, arctan, ln, exp bằng những thuật toán xấp xỉ của giải tích
số. Chính vì vậy tốc độ có thể chậm hơn hàng chục lần so với các phép toán trên số nguyên.
19
Để khắc phục nhược điểm đó, trên các bộ vi xử lý mới, người ta gắn thêm một chip nhỏ bên

trong gọi là bộ đồng xử lý toán học, (ta hay gọi tắt là bộ 80x87), chíp này phụ trách xử lý các
phép toán trên số thực với tốc độ cao và cung cấp nhiều kiểu số thực khác nhau.
BP7 đã có khả năng sinh mã để tận dụng được các kiểu của 80x87 nếu ta đặt dẫn hướng biên
dịch {$N+} hay đặt tuỳ chọn Options/Compiler/8087-80287, khi đó ta có thể sử dụng những
kiểu số thực mà ta sẽ khảo sát dưới đây, lưu ý là phạm vi biểu diễn của các kiểu số thực là rất
lớn nên tạm thời ta không khảo sát phạm vi biểu diễn mà chỉ khảo sát hai thông số quan trọng
nhất của các kiểu số thực, đó là: Số chữ số có nghĩa và số byte biểu diễn một số thực.
Kiểu Số chữ số có nghĩa Số Byte
Real 11-12 6
Single 7-8 4
Double 15-16 8
Extended 19-20 10
Comp 19-20 8
Số chữ số có nghĩa là số chữ số tin cậy của một số thực, tính toán trên các số quá lớn hay quá
nhỏ thì độ tinh cậy của kết quả càng thấp. Lưu ý kiểu Real không phải là kiểu số thực của
80x87.
{$N+}
program FloatingPointTest;
var
a, b ,c: Single;
begin
a := 8765432; b := 8765433;
c := a + b;
Writeln(c:1:1);
Readln;
end.
Chương trình trên cho kết quả là 17530864 (sai số) nếu thay a, b, c đều là Extended thì sẽ
được kết quả chính xác là 17530865, đó là một ví dụ cho thấy kiểu Extended có số chữ số tin
cậy nhiều hơn kiểu Single.
Điều sau đây cần hết sức lưu ý đối với các lập trình viên, khác hẳn với vấn đề ép kiểu

trong số nguyên:
• Trong chế độ biên dịch {$N+}, tất cả các phép tính trên số thực (số dấu chấm động) đều
được ép kiểu thành Extended để tính, Ví dụ: nếu có phép gán a := b * c với a là biến Real
còn b, c là các số thực kiểu nào đó thì máy sẽ tính b * c như một phép nhân hai biến kiểu
Extended, được bao nhiêu đem ép kiểu thành Real rồi gán cho a (nếu có tràn số thì do
phép gán này chứ phép nhân thì khó mà tràn được, trừ khi cố tình).
• Trong chế độ biên dịch {$N-}, thì chỉ có mỗi kiểu Real là dùng được nên mọi biểu thức số
thực đều bị ép kiểu thành Real. Nếu ta viết trong chương trình là 9E37+9E37 sẽ bị báo lỗi
tràn Real ngay.
Khi máy không có 80x87, BP7 có cơ chế giả lập bộ đồng xử lý toán học như giả lập các tính
toán trên kiểu Real. Để sử dụng bộ giả lệnh, ta đặt dẫn hướng biên dịch {$E+} hay chọn
Options/Compiler/Emulation. Khi {$E+} tất cả các lệnh thao tác số thực, cũng như các kiểu
số thực đều được giả lập nên chương trình có thể chạy được trên cả các máy không có 80x87,
còn nếu đặt {$E-} thì nếu chạy chương trình trên máy không có 80x87 sẽ bị báo lỗi:
"Numeric co-processor required" rồi dừng ngay.
20
Về lý thuyết, sử dụng bộ giả lệnh thì chậm hơn 80x87 là dể hiểu, tuy nhiên trên các máy tính
tốc độ cao hiện nay, bộ giả lập số thực trên 80x86 cũng đạt tốc độ rất cao. Trên các CPU PIII,
tôi đã khảo sát và thấy bộ giả lệnh tốc độ còn nhanh hơn 80x87. Vậy nên nếu sử dụng các tính
toán số thực, để đạt độ chính xác cao, ta nên đặt dẫn hướng biên dịch {$N+,E+}
Kinh nghiệm:
a) Kiểu số thực nào nhanh hơn ?
Nếu cho điểm 100/100 cho kiểu số thực tính nhanh nhất thì trong chế độ {$N+,E+}, có thể
cho điểm một cách tương đối như sau:
Single: 087/100; Double: 086/100; Extended: 100/100; Comp: 063/100; Real: 006/100
Vậy nên, tuy Real là một kiểu số thực chuẩn của mọi thế hệ PASCAL, nhưng khi viết chương
trình, nếu cần tiết kiệm bộ nhớ, thay vì dùng Real, ta nên dùng Single hay Double, nếu cần độ
chính xác cao, ta nên dùng Extended. Ngoại trừ kiểu Real và Comp, đối với phép cộng và
nhân, các kiểu số thực còn lại đều tính nhanh hơn cả LongInt.
b) Kiểu Comp

Comp thực chất là Integer 64 bít, cũng giống như ShortInt là Integer 8 bit, LongInt là Integer
32 bít.
• ShortInt: -2
7
→ 2
7
-1
• Integer: -2
15
→ 2
15
-1
• LongInt: -2
31
→ 2
31
-1
• Comp: -2
63
→ 2
63
-1
Nhưng Comp lại được ép kiểu thành Extended để tính toán như đã nói ở trên.
Phép gán a := 20/3 với a là biến Comp thực chất là lấy 20/3 rồi làm tròn lấy giá trị (7) gán cho
a. Comp không lưu trữ được số thập phân có phần lẻ. Mục đích của Comp là tạo một kiểu
nguyên lớn hơn cả LongInt để thực hiện những phép tính trên các số có từ 10 đến 18 chữ số
(hơn nữa thì không chính xác bởi các phép tính được tính trên Extended).
c) Ngăn xếp 80x87
Khi viết các hàm đệ quy trả về giá trị là kiểu số thực của 80x87 phải hết sức đề phòng lỗi tràn
ngăn xếp. Bởi khi tính toán các hàm này, để tận dụng tốc độ, BP7 đã lưu các kết quả trung

gian trực tiếp vào ngăn xếp 80x87 (một vùng nhớ nhỏ nằm ngay trong bộ đồng xử lý toán
học). Ta xét một ví dụ: Viết một hàm đệ quy tính số Fibonacci thứ 32:
{$N+}
program Fibonacci1;
function F(n: Integer): Extended;
begin
if n <= 2 then F := 1
else F := F(n - 1) + F(n - 2);
end;
begin
Writeln(F(32):0:0);
end.
Số Fibonacci thứ 32 là 2178309, (chưa phải là số quá lớn) nhưng chương trình trên sẽ gây lỗi
tràn do không đủ bộ nhớ trong ngăn xếp 80x87 để chứa các giá trị trung gian trong dây
21
chuyền đệ quy. Để khắc phục lỗi này, trong mã lệnh hàm F, ta lưu các giá trị trung gian: F(n -
1) và F(n - 2) vào 2 biến địa phương rồi thực hiện phép cộng trên hai biến địa phương này.
{$N+}
program Fibonacci2;
function F(n: Integer): Extended;
var
x, y: Extended; {Hai biến địa phương để lưu giá trị trung gian}
begin
if n <= 2 then F := 1
else
begin
x := F(n - 1); y := F(n - 2); {Tính ra hai biến trung gian}
F := x + y; {Sau đó cộng lại}
end;
end;

begin
Writeln(F(32):0:0);
end.
IV.7. Các dẫn hướng biên dịch khác.
• Có rất nhiều dẫn hướng biên dịch trong BP7, có thể đọc bằng cách chọn menu:
Help/Compiler Directives.
• Để xem tất cả các dẫn hướng biên dịch đang được sử dụng mặc định, bấm: Ctrl + O + O
(hai chữ O chứ không phải số không), sau đó có thể sửa
• Ta có thể đặt nhiều dẫn hướng biên dịch tại những thời điểm khác nhau, nhưng kinh
nghiệm đặt các dẫn hướng biên dịch là: Nếu trong giai đoạn chạy thử và sửa lỗi thì tính an
toàn và khả năng bắt lỗi được ưu tiên trước hết. Còn khi đã thành phẩm thì tốc độ và tiết
kiệm tài nguyên là những chỉ tiêu được đưa lên hàng đầu.
V. MỘT VÀI KỸ THUẬT NHỎ TRONG BP7
V.1. Break, Continue, Exit, Halt và Goto.
Trong một vòng lặp, nếu gặp lệnh Break thì vòng lặp đó sẽ bị ngừng vô điều kiện.
Ví dụ:
Nhập vào hai số x và y, tính thương của x/y. Quá trình cứ tiếp tục cho tới khi nhập y = 0:
repeat
Readln(x, y);
if y = 0 then Break;
Writeln(x / y);
until False; {Không cần điều kiện thoát}
Trong một vòng lặp, nếu gặp lệnh Continue thì toàn bộ lệnh của vòng lặp nằm phía sau
continue sẽ bị bỏ qua, máy kiểm tra ngay điều kiện xem còn lặp tiếp không, nếu còn lặp tiếp
thì làm lần lặp kế tiếp luôn.
Ví dụ:
Nhập vào số x kiểu LongInt và kiểm tra dữ liệu Abs(x) ≤ 46340; nếu đúng, in ra x
2
, nếu
không bắt nhập lại. (Bởi 46341

2
tràn phạm vi LongInt)
22
repeat
Readln(x);
if Abs(x) > 46340 then Continue;
Writeln(Sqr(x));
until Abs(x) <= 46340;
Trong một chương trình con, nếu chạy tới lệnh Exit thì chương trình con sẽ thoát ra ngay.
Chạy tới lệnh Halt ở bất cứ nơi đâu, chương trình sẽ dừng vô điều kiện. (Điều này tương
đương với lệnh Exit nằm ở thân chương trình chính}
Bốn lệnh kể trên không có ở mọi thế hệ PASCAL. Nói chung cũng có những cấu trúc tương
đương để thay, nhưng dùng được các lệnh này một cách uyển chuyển và hợp lý sẽ làm cho đỡ
tốn công sức nhiều, (đặc biệt là Break và Exit).
Để dễ nhớ công dụng, ta có thể quan niệm như sau: Break là Goto tới một nhãn ngay phía
dưới bên ngoài vòng lặp. Continue là Goto đến điểm kiểm tra điều kiện lặp. Exit là Goto tới
cuối chương trình con (chữ End;). Còn Halt thực sự không phải lệnh Goto.
Nói chung, Goto là lệnh phi cấu trúc nên ta cố gắng sử dụng càng hạn chế càng tốt, bằng cách
thay nó bằng các lệnh kể trên và các lệnh có cấu trúc. Tuy nhiên, cũng không nên quá cứng
nhắc, nếu muốn thoát vô điều kiện cùng một lúc 7, 8 vòng lặp For thì nếu không dùng Goto,
ta sẽ phải thay bằng các cấu trúc lặp với số lần không biết trước (Repeat, While) hay sử dụng
7, 8 lệnh Break, chương trình sẽ trở nên khó đọc hơn và chạy chậm hơn. Hạn chế không có
nghĩa là cấm dùng (cũng như đệ quy vậy thôi), ta phải xác định được nhược điểm của Goto để
viết các chú thích đúng chỗ giúp cho chương trình sáng sủa và dễ bảo trì.
V.2. Hệ cơ số 16 (hexa)
Trong hệ cơ số 16, có 16 chữ số, ký hiệu như sau: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F.
Về mặt giá trị các chữ số từ A tới F mang giá trị từ mười đến mười lăm.
Để chuyển đổi từ hệ thập phân sang hệ 16 ta có thể dùng phép chia liên tiếp với số chia là 16.
Ví dụ: Đổi số 43982 sang hệ 16
♦ 43982 / 16 = 2748 dư 14 (dư E)

♦ 2748 / 16 = 171 dư 12 (dư C)
♦ 171 / 16 = 10 dư 11 (dư B)
♦ 10 / 16 = 0 dư 10 (dư A).
Vậy số 43982 sẽ bằng ABCE trong hệ 16, trong BP7, viết $ABCE cũng như là viết 43982.
Đổi số từ hệ hexa sang hệ thập phân có thể đổi bằng cách nhân từng chữ số Hexa với các luỹ
thừa tương ứng của 16.
$ABCE = 10*16
3
+ 11 * 16
2
+ 12 * 16 + 14 = 43982.
Để đổi một số trong hệ nhị phân ra hệ Hexa, ta xét từ hàng đơn vị lên, đổi từng nhóm 4 chữ số
nhị phân được một chữ số hệ 16.
10 1010 1011 1100 1110
(2)
= 2ABCE
(16)
Phép chuyển ngược lại, ta đổi một chữ số Hexa thành nhóm 4 chữ số nhị phân. Chứng minh
điều trên khá dễ dàng.
Xét hai lệnh gán sau với I là biến Integer:
I := $0000FFFF
I := $FFFFFFFF
Mới trông thì tưởng như $0000FFFF < $FFFFFFFF, nên lệnh gán thứ nhất tràn số ($FFFF =
65535) thì lệnh gán thứ hai sai là tất nhiên, nhưng thực ra không phải như vậy.
23
Các hằng số hexa 16 bít (

4 chữ số hexa) được BP7 coi như các giá trị LongInt 32 bit
$0000FFFF = 0000 0000 0000 0000 1111 1111 1111 1111 = 65535 thì tràn Integer
$FFFFFFFF = 1111 1111 1111 1111 1111 1111 1111 1111 = -1. (Không tràn)

Ta nhớ lại cách lưu trữ giá trị LongInt trong máy tính, bít đầu tiên = 1 tức là số âm và giá trị
số âm đó sẽ bằng giá trị dãy 31 chữ số tiếp theo trừ đi 2
31
. nên kết quả sẽ bằng -1. Viết số -1
trong hệ Hexa là viết như vậy đấy, nếu như không muốn dùng ký pháp không chính tắc -$1.
V.3. Tổ chức bộ nhớ trong chế độ thực
Mọi byte nhớ trong chế độ thực đều được đánh địa chỉ (như đánh số nhà vậy). Hệ thống địa
chỉ được đánh số từ 0 cho tới $FFFFF (Từ 0 tới 1048575, có 2
20
= 1MB ô nhớ) bộ nhớ này
được chia làm 16 đoạn (Segments)
Đoạn 0: Từ $00000 tới $0FFFF
Đoạn 1: Từ $10000 tới $1FFFF
Đoạn 2: Từ $20000 tới $2FFFF

Đoạn F: Từ $F0000 tới $FFFFF.
Mỗi đoạn có $10000 = 65536 = 64KB ô nhớ được đánh địa chỉ, các biến toàn cục trong
Pascal được tổ chức trong một đoạn, các biến địa phương được đặt vào một đoạn, mã lệnh đặt
vào một đoạn. (Ta tạm hình dung như vậy chứ thực ra các đoạn có thể đặt chồng chéo, không
nhất thiết phải bắt đầu và kết thúc ở các địa chỉ nói trên và có nhiều cách viết tương đương
cho địa chỉ của một ô nhớ). Mỗi biến chiếm một đoạn liên tục các ô nhớ và địa chỉ của ô nhớ
đầu tiên là địa chỉ của biến đó. Vì mỗi đoạn giới hạn ở dung lượng 64KB nên tổng kích
thước các biến toàn cục cũng như tổng kích thước biến địa phương của một chương trình con
không thể vượt quá giới hạn này. Nếu muốn dùng các đoạn nhớ còn trống khác thì ta phải sử
dụng biến cấp phát động và con trỏ.
Khi không có thiết lập về bộ nhớ (Tự tra cứu dẫn hướng biên dịch $M), thì ngoài vùng nhớ bị
hệ thống chiếm giữ, chương trình của chúng ta độc chiếm hoàn toàn vùng nhớ còn lại.
V.4. SizeOf, FillChar và Move
Bây giờ ta tạm quên đi kiểu của các biến mà chỉ quan tâm đến độ lớn của biến. Mặc dù
PASCAL là ngôn ngữ định kiểu rất mạnh, nhưng suy cho cùng, khi dịch chương trình ra mã

máy thì mỗi biến chỉ đơn giản là một dãy ô nhớ chứa các giá trị số mà thôi. Chẳng hạn như
cùng một ô nhớ chứa số 65, nếu ô nhớ đó do một biến kiểu Byte chiếm thì khi truy xuất biến
đó ta có giá trị 65, nhưng nếu là biến kiểu Char chiếm thì khi truy xuất ta có chữ A, là biến
kiểu Boolean chiếm thì khi truy xuất ta có giá trị TRUE. Vì vậy ta gọi chung kiểu Boolean,
Char, Byte, ShortInt là các biến 1 Byte; Integer, Word là các biến 2 Byte; v.v
a) Hàm SizeOf cho ta độ lớn đo bằng Byte của một biến hay một kiểu.
Ví dụ:
Nếu
var
a: Integer;
b: Char;
c: array[1 10] of LongInt
Thì
24
SizeOf(a) = SizeOf(Integer) = 2
SizeOf(b) = SizeOf(Char) = 1
SizeOf(c[1]) = SizeOf(LongInt) = 4
SizeOf(c) = 10 * 4 = 40.
b) Thủ tục FillChar(X , S, V). Điền giá trị V (1 byte) vào bộ nhớ, bắt đầu từ địa chỉ biến X,
điền liên tiếp S byte. (V phải là một giá trị 1 Byte).
Ví dụ:
Nếu
var
a: array[1 10] of Boolean;
b: array[1 10] of Byte;
c: array[1 10] of Char;
Thì:
♦ FillChar(a, 10, 65) tương đương với FillChar(a, SizeOf(a), 65); sẽ cho ta một mảng a
toàn giá trị TRUE. Lưu ý rằng số 65 ở đây có thể thay bằng một số kiểu Byte khác 0
bởi Pascal coi Byte khác 0 là giá trị True còn Byte bằng 0 là giá trị False

♦ FillChar(b, 10, 'B') tương đương với FillChar(b, SizeOf(b), 'B'); sẽ cho ta một mảng b
toàn các số 66
♦ FillChar(c, 10, 67) tương đương với FillChar(c, SizeOf(c), 67); sẽ cho ta một mảng c
toàn các chữ C.
Đặc biệt lưu ý:
Nếu x là mảng các phần tử lớn hơn 1 Byte :
x: array[1 100] of Integer;
Muốn khởi tạo tất cả các giá trị trong mảng x thành 0, ta có thể viết:
FillChar(x, SizeOf(x), 0)
Nhưng muốn khởi tạo tất cả các giá trị trong mảng x thành 1, ta không thể viết:
FillChar(x, SizeOf(x), 1)
Bởi FillChar là điền giá trị vào từng Byte một, nên do mỗi biến Integer là 2 Byte nên cả hai
Byte này đều sẽ bị điền giá trị 1.
x[1] = 00000001 00000001 = 257.
Nếu điền giá trị 0 thì cả hai Byte đều bị điền 0, nên giá trị Integer chiếm 2 Byte đó cũng sẽ là
0, mọi chuyện vẫn ổn, nhưng giá trị khác 0 thì không thể cho kết quả như ý được.
FillChar là một công cụ mạnh, ngoài chuyện khó chịu kể trên, nó nhanh hơn hàng ngàn lần
cách dùng vòng lặp để điền từng giá trị một, nên tận dụng đúng lúc đúng chỗ.
c) Thủ tục Move(A, B, S): Copy S byte từ vùng nhớ bắt đầu ở địa chỉ biến A sang vùng nhớ
bắt đầu bằng địa chỉ biến B.
Ví dụ:
Nếu
var
a, b: array[1 1000] of Word;
Thì:
Move(a, b, 2000) sẽ copy giá trị toàn mảng a sang mảng b
Move(a[501], b[1], 1000) sẽ copy nửa cuối của mảng a sang nửa đầu của mảng b
25

Tài liệu bạn tìm kiếm đã sẵn sàng tải về

Tải bản đầy đủ ngay
×