1. Trang chủ
  2. » Công Nghệ Thông Tin

Lập trình cấu trúc

8 1,1K 2
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Lập trình cấu trúc
Tác giả Lí Minh Hoăng
Trường học Trường Đại Học
Chuyên ngành Lập trình
Thể loại Bài viết
Định dạng
Số trang 8
Dung lượng 105,5 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

Lập trình cấu trúc

Trang 1

Lậ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 2

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

- Đặ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 3

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/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 4

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/ 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 5

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ì

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 7

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

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 8

Như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

Ngày đăng: 10/09/2012, 15:26

TỪ KHÓA LIÊN QUAN

w