Lập trình cấu trúc
Trang 1Lập trình cấu trúc trín BP7
Lí Minh Hoăng
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ỗí vă 'nếu có lỗi thì tìm vă sửa thế nẳ.
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 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 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
Trang 2hế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
- Đặ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 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:
Trang 3Bướ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/Ađ 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 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.
Trang 44 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/ Ađ 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
I Một vài kỹ thuật nhỏ trong BP7
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 x2, nếu không bắt nhập lại (Bởi 463412 tràn phạm vi LongInt)
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
Trang 5tươ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ì
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*163 + 11 * 162 + 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
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 231 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
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
Trang 6địa chỉ được đánh số từ 0 cho tới $FFFFF (Từ 0 tới 1048575, có 220 = 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
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ì
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;
Trang 7b: 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
Thông thường, để an toàn và dễ sửa đổi, lệnh Move(a[501], b[1], 1000) nên thay bằng: Move(a[501], b[1], 500 * SizeOf(a[1]));
Nói chung, chỉ nên copy bộ nhớ giữa hai biến chiếm có kiểu chiếm cùng một số Byte, nếu a là mảng Byte và b là mảng Word thì ta lại gặp vấn đề khó chịu như FillChar Xét ví dụ sau:
var
a: array[1 1000] of Word;
Nếu muốn dồn các phần tử từ a[2] tới a[999] lên trước một vị trí ta có thể dùng vòng lặp: for i := 2 to 999 do a[i - 1] := a[i];
Nếu muốn dồn các phần tử từ a[2] tới a[999] về sau một vị trí ta có thể dùng vòng lặp: for i := 999 downto 2 do a[i + 1] := a[i];
Trang 8Nhưng nếu dùng lệnh Move, ta có thể tự do copy ô nhớ mà không phải bận tâm to với downto gì cả.
Move(a[2], a[1], 998 * 2) và Move(a[2], a[3], 998 * 2);
Đặc biệt lưu ý:
Dùng Move thì số Byte di chuyển không được phép là số âm,
5 Biến ở địa chỉ tuyệt đối.
Như đã nói ở trên, bộ nhớ được chia làm các đoạn, như vậy một ô nhớ sẽ có địa chỉ xác định xem nó nằm ở đoạn nào (địa chỉ đoạn − Segment) và trong đoạn đó nó là ô nhớ thứ mấy (địa chỉ Offset)
Để khai báo một biến bắt buộc chiếm giữ ở địa chỉ nào đó, ta dùng cách khai báo biến nằm ở địa chỉ tuyệt đối;
Ví dụ:
var
Time: LongInt absolute 0:$46C;
Có nghĩa là: Khai báo biến Time nằm ở một ô nhớ của phân đoạn 0 và địa chỉ Offset
$46C; absolute là từ khoá của BP7 Biến Time khai báo như vậy sẽ không chiếm bộ nhớ của chương trình mà sẽ được dùng để truy xuất vào ô nhớ chỉ ra bởi địa chỉ nói trên.
Các biến của BIOS (Basic Input Output System − Hệ thống vào ra cơ sở) có sẵn trong máy tính được đặt ở đầu phân đoạn 0 của bộ nhớ Địa chỉ Offset $46C chứa một biến
LongInt là đồng hồ xung nhịp của máy tính Đồng hồ này chứa bộ đếm thời gian bắt đầu
với giá trị 0 từ 0 giờ và với tần suất 18.2 lần trong một giây, biến LongInt này được tăng lên 1* Vì vậy cứ 18.2 lần trong một giây, biến Time của ta sẽ tự động tăng lên 1 mà chẳng cần lệnh gì cả nên không hề ảnh hưởng gì đến tốc độ Ta có thể lợi dụng tính chất
đó để đo xem chương trình của ta chạy mất bao nhiêu giây
program TimeCount;
var
Time: LongInt absolute 0:$46C;
Start: LongInt;
…
begin
Start := Time; {Lưu thời điểm lúc bắt đầu chạy chương trình vào biến Start}
…{Mã lệnh chương trình}
Writeln(’Thời gian: ’, (Time - Start) / 18.2:1:2, ’ (giây)’);
end