Để một chương trình khác có thể sử dụng được hμm tạo bảng chọn, phần giao diện phải cung cấp mẫu hμm BangChon vμ các dữ liệu đầu vμo như vị trí, số mục chọn, mảng các tên mục chọn vμ bộ
Trang 12.1.3 Viết lần l−ợt tên các mục chọn
2.1.4 Lμm nổi bật một mục chọn mặc định
2.1: Vẽ bảng chọn ban đầu
2.2.1: Xử lí khi
gõ mũi tên lên, xuống
2.2.2: Xử lí khi
gõ các phím khác
2.2: Xử lí gõ phím
Hình 11.7: Chi tiết mức 3 các công việc 2.1 vμ 2.2
Đến đây ta thấy hầu nh− mọi công việc đã đủ đơn giản Phần duy nhất
cần lμm chi tiết thêm lμ mô đun 2.2.1 - xử lí khi gõ các phím mũi tên Up, Down
2.2.1.2: Cập nhật lại mục chọn i mới
- Gán i:= i + 1 / i:= i - 1 tuỳ theo gõ Up, Down
- Chú ý "xoay vòng tròn" khi: gõ phím mữi tên Up khi đang ở mục chọn đỉnh bảng (i=0) hay gõ mũi tên Down khi đang ở mục chọn đáy bảng (i = số mục chọn -1)
Trang 22.2.1.3: Lμm nổi bật mục chọn i mới Hiển thị mục chọn i bằng mμu
chọn
2.2.1: Xử lí khi
gõ mũi tên lên, xuống
2.2.1.1 Lμm chìm mục chọn cũ
2.2.1.2 Cập nhật mục chọn mới
2.2.1.3.Lμm nổi mục chọn mới
Hình 11.7: chi tiết mức 4 công việc 2.2.1
Tóm lại, sau bốn bước chi tiết dần, các công việc đã trở thμnh đơn giản Việc triển khai viết chương trình sử dụng Turbo Pascal hay một ngôn ngữ lập trình nμo đó chỉ đơn thuần lμ chuyển từng công việc thμnh các câu lệnh tương ứng một cách máy móc
7.3 Chương trình chi tiết
Dưới đây lμ chương trình tạo bảng chọn nhận được sau khi triển khai sơ đồ trên thμnh các câu lệnh Turbo Pascal
uses crt;
const max_SoMucChon = 8; {số mục chọn tối đa}
type mang_string = array[0 max_SoMucChon-1] of string;
var Ten_Muc_Chon: mang_string; {mảng các tên muc chọn} So_Muc_Chon: word; {số mục chọn}
Mau_nen, Mau_Chon: byte;
Chon: integer; {danh so tu 0 (SoMucChon - 1),ESC = -1} Procedure NhapThamSoBangChon;
{phần nhập dữ liệu}
begin
So_Muc_Chon:=3;
Ten_Muc_Chon[0]:=' muc chon 1 ';
Ten_Muc_Chon[1]:=' muc chon 2 ';
Trang 3for i:=1 to SoMucChon -1 do
if rong < length(TenMucChon[i]) then
rong:= length(TenMucChon[i]);
window(x,y,x+rong,y+SoMucChon);
Trang 4enter: begin BangChon:= i; exit end;
esc : begin BangChon:=-1; exit end;
Trang 5Để một chương trình khác có thể sử dụng được hμm tạo bảng chọn,
phần giao diện phải cung cấp mẫu hμm BangChon vμ các dữ liệu đầu vμo
như vị trí, số mục chọn, mảng các tên mục chọn vμ bộ mμu (mμu nền bình thường vμ mμu khi được chọn)
Nhận thấy rằng các tham số nói trên đều đã có trong danh sách tham
số của hμm bảng chọn Các kiểu dữ liệu word, byte lμ các kiểu chuẩn đã
được định nghĩa sẵn Chỉ có kiểu mang_string mảng các xâu kí tự không
phải lμ một kiểu chuẩn nên phải khai báo nó trong phần giao diện, sau từ
khoá Interface
Phần triển khai chi tiết của Unit không có gì khác hơn lμ thực hiện chi
tiết hμm bảng chọn như đã viết trong ví dụ ở tiết 3 Tóm lại, tệp BgChon.pas
có nội dung như sau
Trang 6end; {function BangChon}
END {Unit BgChon}
Biên dịch tệp BgChon.pas thμnh tệp BgChon.tpu vμ đặt vμo thư mục
các Units chuẩn của Turbo Pascal như đã hướng dẫn ở trên Bây giờ có thể kiểm tra thử hoạt động của bảng chọn bằng một chương trình ngắn gọn như sau
Trang 7Bμi toán: Xây dựng chương trình quản lí hồ sơ của sinh viên Chương
trình cần có các chức năng tạo tệp dữ liệu mới, mở tệp dữ liệu đã có, liệt kê toμn bộ danh sách, tìm kiếm thông tin về một sinh viên nμo đó, thêm một sinh viên (vμo cuối) danh sách Chưa xét đến việc sửa đổi thông tin, xoá tên một sinh viên khỏi danh sách hay sắp xếp danh sách theo môt trật tự nμo đó khi thêm vμo
Chương trình gồm hai phần, phần giao diện lμ một bảng chọn vμ phần
xử lí các yêu cầu của người sử dụng
Ta đã xây dựng Unit lμm bảng chọn nên bây giờ chỉ cần gọi ra sử dụng Dưới đây sẽ phân tích chi tiết phần xử lí các yêu cầu Rõ rμng cần chia
ra 4 mô đun thực hiện 4 chức năng công việc chính như đặc tả ở trên
1 - Mở tệp mới hoặc tệp đã có trên đĩa
- Đầu vμo: tên tệp cần mở
- Đầu ra: tệp được mở ra sẵn sμng để đọc ra hoặc săn sμng để viết vμo nếu lμ tạo tệp mới
Trang 82 - Liệt kê nội dung toμn bộ các bản ghi trong tệp
- Đầu vμo: tệp đã mở sẵn sμng
- Đầu ra: hiển thị toμn bộ nội dung các bản ghi có trong tệp ra mμn hình
3 - Tìm kiếm: tìm một bản ghi cụ thể, hiển thị kết quả tìm kiếm lên mμn hình
- Đầu vμo: tệp đã mở sẵn sμng, mã số của sinh viên cần tìm gõ từ bμn phím
- Đầu ra: hiển thị nội dung bản ghi tìm thấy hoặc thông báo không tìm
thấy
4- Thêm một bản ghi vμo cuối tệp
- Đầu vμo: tệp đã mở sẵn sμng, mã số của sinh viên vμ các thông tin khác nhập từ bμn phím
- Đầu ra: nếu sinh viên chưa có trong danh sách thì bản ghi mới được thêm vμo cuối tệp
Giả sử tên của 4 thủ tục thực hiện các công việc tương ứng lμ Mở tệp, Liệt kê, Tìm kiếm, Thêm Ta có thể viết ngay chương trình chính, chỉ bao
gồm một số lời gọi các mô đun chương trình con như dưới đây
Chương trình sử dụng lại chương trình con lμm bảng chọn mμ ta đã chuyển thμnh Unit
Trang 92- Liệt kê toμn bộ danh sách
Chi tiết các công việc như sau
2.1 - Trình bμy phần tiêu đề để hiển thị danh sách
2.2 - Đưa cửa sổ tệp về đầu tệp
2.3 - Đọc bản ghi vμo biến trung gian vμ viết ra mμn hình
2.4 - Dịch xuống bản ghi tiếp
3.2.1- Đưa cửa sổ tệp về đầu tệp
3.2.2 - Đọc từng bản ghi vμo biến trung gian
3.2.3 - So sánh với mã số cần tìm 3.2.4 - Dừng khi đã thấy hoặc hết tệp
3.3 - Hiển thị kết quả
3.3.1 - Trường hợp tìm thấy, hiển thị nội dung biến trung gian 3.3.2 - Trường hợp không tìm thấy, hiển thị thông báo
3.4 - Hỏi lại người dùng có cần tìm kiếm tiếp hay không
3.5 - Xử lí tuỳ theo yêu cầu của người sử dụng
3.5.1 - Có: lặp lại việc tìm kiếm 3.5.2 - Không: kết thúc
Trang 104.5 - Xử lí câu trả lời:
4.5.1 Có: lặp lại từ 4.1 4.5.2 Không: sang bước tiếp theo
4.6 - Đóng tệp vμ kết thúc
8.2 Triển khai chi tiết chương trình
Dưới đây lμ các mô đun chương trình con thực hiện 4 chức năng công việc chính như đã phân tích
Writeln(' Ban muon tao tep moi ? Enter=Yes ');
if ReadKey = Enter then Rewrite(ds)
begin
Trang 11reset (TepHoSo); TimThay:=false;
while (not TimThay) and (not eof(TepHoSo)) do
writeln(' Sv co ma so: ',ma,': ');
writeln(' Ho ten: ',sv.hoten);
writeln(' Nam sinh: ',sv.namsinh);
Trang 12write(' Ho ten: ');readln(hoten);
write(' Nam sinh ( 2 so cuoi ):
đã xây dựng trong Unit BgChon ở chương trước
Tóm lại, ta nhận được chương trình hoμn chỉnh như sau đây
Trang 13TenMC[2]:=' Tim kiem ';
TenMC[3]:=' Them vao danh sach ';
TenMC[4]:=' Ket thuc ';
Trang 14END
Câu hỏi vμ bμi tập
1 Nguyên tắc chung khi thiết kế chi tiết dần từng bước lμ gì
2 Các mô đun trong một chương trình liên kết với nhau bằng cách nμo
3 Sơ đồ cấu trúc chưong trình khác gì với lưu đồ thuật giải
4 Khi nμo thì có thể vμ cần dùng thuật giải đệ quy
5 So sánh thuật giải đệ quy với thuật giải lặp.Thuật giải nμo hiệu quả hơn
6 Câu lệnh nμo luôn có trong một thủ tục đệ quy
7 Tên của mọt hμm đệ quy phải xuất hiện ít nhất lμ bao nhiêu lần bên trong phần thân của nó
8 Tại sao nếu n lμ tham số của một hμm, thủ tục đệ quy thì n phải lμ tham
NhapDuLieu, PhanLoai, DiemTB, XetLenLơp Cho trước chương trình
chính như sau
NhapDuLieu; {thủ tục nhập dữ liệu}
PhanLoai (4,6,8); {kém:0 4; trung bình:5,6; khá 7,8; giỏi: 9,10} Writeln(' Điểm trung bình cả lơp: ', DiemTB);
Writeln(' Danh sach học sinh được lên lớp ');
XetLenLơp
5 Viết hμm đệ quy tính số Fibonaci
Trang 156 Viết thủ tục đệ quy in các phần tử mảng theo thứ tự xuôi, theo thứ tự
ngược
7 Liệt kê mọi hoán vị của n phần tử dùng thuật giải đệ quy
8 Viết thủ tục đệ quy tìm nghiệm bằng phương pháp chia đôi dần của
phương trình y = exp(x) + sin x - 2 = 0 trên đoạn [0,1]
Trang 16Chương 12 Con trỏ vμ cấu trúc dữ liệu động
Cho đến nay ta biết rằng muốn sử dụng một biến trong chương trình
hay chương trình con thì phải khai báo Sau khi đã khai báo, tức lμ đã đăng kí
tên vμ kiểu dữ liệu của biến ở đầu chương trình, chương trình con thì ta có
thể yên tâm sử dụng mμ không cần phải lμm thêm bất cứ viêc nμo khác Mọi
công việc về quản lí các biến sẽ do trình biên dịch vμ hệ điều hμnh đảm
nhiệm Chúng sẽ được cấp phát vùng nhớ khi bắt đầu chương trình, chương
trình con vμ được giải phóng khi chương trình, chương trình con kết thúc
Như vậy, biến toμn cục sẽ tồn tại trong suốt thời gian hoạt động của chương
trình chính, biến cục bộ sẽ tồn tại trong suốt thời gian hoạt động của chương
trình con dù rằng có thể đã được sử dụng xong sớm hơn ngay từ lúc bắt đầu
chương trình hoặc đến gần thời điểm kết thúc mới cần đến nơi
Vì lí do nμy mμ người ta gọi đó lμ các biến tĩnh Sử dụng các biến tĩnh
dễ dμng vμ đơn giản Tuy nhiên chúng không cho phép tận dụng triệt để bộ
nhớ
Biến động
Để khắc phục nhược điểm trên của các biến tĩnh, người lập trình cần
phải có khả năng can thiệp trực tiếp vμo việc cấp phát vμ giải phóng vùng nhớ
Chỉ xin cấp phát bộ nhớ cho một biến khi bắt đầu thực sự dùng đến nó, cần
bao nhiêu xin cấp phát đúng bấy nhiêu, tránh lãng phí vμ khi dùng xong có
thể giải phóng ngay không để chiếm chỗ vô ích
Khái niệm biến động được đề xuất chính lμ để đáp ứng đòi hỏi trên
Nó mang lại khả năng sử dụng bộ nhớ linh hoạt vμ mềm dẻo hơn, tuy nhiên
cũng yêu cầu người lập trình phải lμm việc nhiều hơn, có trình độ cao hơn
Khái niệm biến động đi đôi với biến con trỏ
Trang 171.2 Định nghĩa và khai báo
1.2.1
1.2.2
Biến con trỏ
Biến con trỏ (Pointer variable) lμ loại biến đặc biệt, chiếm 2 byte Nội
dung của nó không phải lμ một giá trị, một dữ liệu, mμ lμ một địa chỉ Đây lμ
địa chỉ ô nhớ đầu tiên của vùng nhớ để chứa biến động tương ứng Vùng nhớ nμy dμi đến đâu lμ do kiểu dữ liệu của biến động quyết định
Vậy cần chỉ rõ con trỏ kiểu (dữ liệu) gì! Khi một con trỏ có kiểu được
khai báo thì trình biên dịch nhận được các thông tin sau:
Có thể khai báo kiểu con trỏ vμ sau đó khai báo biến con trỏ Cũng có
thể khai báo trực tiếp ngay biến con trỏ, không định nghĩa kiểu
Cú pháp: từ khoá Type, tên kiểu con trỏ, dấu bằng, dấu mũ, kiểu dữ liệu trỏ tới
Type kiểu con trỏ = ^ kiểu dữ liệu ;
Var biến con trỏ : kiểu con trỏ ;
hoặc
Var biến con trỏ : ^ kiểu dữ liêu ;
Ví dụ 1: Type RealPtr = ^ Real;
Var a,b, kq: RealPtr;
hoặc
Var a,b, kq: ^ Real;
Ví dụ 2: Type Sv = Record
Ho_dem: string[24];
Trang 18Var SinhVien1, SinhVien2, LopTruong: ^ Sv;
Lưu ý rằng trong khai báo kiểu hoặc biến con trỏ không chấp nhận kiểu dữ liệu vô danh Sau kí hiệu con trỏ “^” chỉ chấp nhận các kiểu dữ liệu
chuẩn đã có sẵn hoặc các kiểu đã được khai báo trước
Ví dụ, trình biên dịch sẽ không chấp nhận cách khai báo con trỏ đến mảng như sau:
VAR MangPtr: ^ array[1 10] of real;
Thậm chí một khai báo kiểu
TYPE MangPtr = ^ array[1 10] of real;
cũng không hợp lệ Cần phải khai báo kiểu mảng trước đã
TYPE Mang = array[1 10] of real;
MangPtr = ^Mang;
VAR A,B: MangPtr;
Hoặc
TYPE Mang = array[1 10] of real;
VAR A,B: ^MangPtr;
1.3 Các phép toán đối với con trỏ
1.3.1 Gán giá trị cho biến con trỏ
Nội dung của một biến con trỏ lμ một địa chỉ Có thể gán giá trị cho một biến con trỏ, nghĩa lμ cho con trỏ nμy trỏ đến một địa chỉ cụ thể nμo đó bằng các cách như sau
1- Gán trực tiếp địa chỉ của một biến tĩnh cho con trỏ cùng kiểu dữ liệu, dùng toán tử lấy địa chỉ @ Lệnh gán nμy lμm cho biến con trỏ sẽ trỏ
Trang 192- Dùng các thủ tục New hoặc GetMem để khởi tạo biến động vμ gán
giá trị cho con trỏ tương ứng như sẽ xét trong tiết sau
3- Dùng hμm Ptr để chuyển đổi một địa chỉ đoạn vμ một offset thμnh
một địa chỉ rồi gán cho con trỏ (sẽ xét đến trong phần lập trình hệ thống)
4- Có thể gán giá trị của một biến con trỏ cho một biến con trỏ khác cùng kiểu Sau phép gán nμy hai con trỏ có cùng gía trị, nghĩa lμ trỏ đến cùng một biến động Ví dụ,
Chỉ có thể so sánh hai con trỏ bằng nhau hay khác nhau
Hai biến con trỏ lμ bằng nhau nếu cùng kiểu vμ cùng giá trị, tức lμ cùng trỏ đến một địa chỉ
Con trỏ NIL
- NIL Lμ một giá trị đặc biệt, nói rằng con trỏ nμy không trỏ vμo đâu
cả
- Có thể gán giá trị NIL cho bất cứ con trỏ kiểu nμo
Việc đặt ra giá trị NIL nhằm mục đích sau Nội dung của biến con trỏ
được hiểu lμ một địa chỉ Sau khi khai báo biến con trỏ mμ chưa khởi tạo biến
động tương ứng, hoặc khi đã giải phóng biến động, vùng nhớ mμ con trỏ trỏ tới có thể chứa các dữ liệu quan trọng Dữ liệu có thể bị vô tình lμm hỏng nếu
để con trỏ "vu vơ" Việc gán giá trị đặc biệt NIL trong các tình huống trên
nhằm tránh hậu quả bất thường có thể xảy ra
2 Biến động
2.1 Cấp phát vùng nhớ và truy cập biến động
Cấp phát vùng nhớ cho biến động
Cú pháp: Dùng thủ tục New
New ( biến con trỏ );
Thủ tục nμy cấp một vùng nhớ còn rỗi, đủ để chứa một dữ liệu có kiểu tương ứng (của con trỏ) Địa chỉ của ô nhớ đầu tiên sẽ được ghi vμo nội dung của biến con trỏ
Trang 202.2 Giải phóng biến động, thu hồi vùng nhớ
Khi sử dụng xong, nên giải phóng vùng nhớ đã cấp cho biến động
Dispose ( biến con trỏ );
biến con trỏ := NIL;
Thủ tục nμy trả lại phần bộ nhớ mμ biến động đã đ−ợc cấp nghĩa lμ
đánh dấu rằng nó trở thμnh vùng nhớ rỗi, có thể dùng vμo việc khác
Trang 21Trái với con trỏ có kiểu, con trỏ không định kiểu hay con trỏ kiểu
chung - generic pointer - chỉ để trỏ đến một địa chỉ ô nhớ đầu tiên của vùng nhớ mμ ta sẽ cần đến Kích thước của vùng nhớ nμy còn chưa xác định, cấu trúc cũng chưa có Người sử dụng phải tự lo lấy việc ấn định một kích thước
vμ một cấu trúc khi truy cập
Khai báo: Var biến con trỏ: Pointer ;
Xin cấp phát vùng nhớ: GetMem ( biến con trỏ , kich thước );
Giải phóng vùng nhớ: FreeMem ( biến con trỏ , kích thước );
ở đây kích thước lμ kích thước khối nhớ liên tục tính bằng word
ứng dụng
3.2
Khi cần xử lí cả một khối nhớ liên tục, không cần truy cập đến từng
thμnh phần, ví dụ cần xử lí một lượng gồm size * Word dữ liệu, chẳng hạn
một tệp hình ảnh, tệp âm thanh, một mμn hình, một khung cửa sổ, thì để tăng tốc độ truy cập người ta thường dùng con trỏ không định kiểu
Các chương trình đồ hoạ vμ tạo hoạt hình thường phải thực hiện nhiều lần việc lưu giữ rồi lại cho hiển thị toμn bộ hay một phần mμn hình Ta sẽ thấy nhiều ví dụ về sử dụng con trỏ không định kiểu ở đó
Cũng do tính chất không định kiểu hay nói đúng hơn lμ chưa định kiểu
mμ con trỏ chung Pointer có thể tương thích với mọi kiểu dữ liệu bất kì Ta
có thể dùng phép ép kiểu để truy cập vùng nhớ trỏ bởi con trỏ chung Pointer
như đây lμ một biến có kiểu cụ thể nμo đó
Dưới đây lμ một ví dụ minh hoạ, sử dụng con trỏ không định kiểu vμ
thủ tục GetMem xin cấp phát động vùng nhớ để tiết kiệm khi dùng mảng
Ta đã biết rằng khi khai báo biến mảng thì kích thước phải lμ hằng cho trước
Điều nμy dẫn đến phải khai báo dự phòng kích thước tôí đa Sử dụng con trỏ
vμ biến động ta không dùng biến tĩnh mμ dùng một con trỏ không định kiểu Khi biết kích thước cụ thể sẽ xin cấp vừa đủ vùng nhớ cần thiết Việc truy cập từng phần tử mảng sẽ thông qua phép ép kiểu
Chương trình dưới đây tạo vμ in ra một ma trận vuông có các phần tử
lμ các số nguyên, sinh ngẫu nhiên Kích thước (số hμng = số cột) của ma trận gõ từ bμn phím
Trang 22write(' cho kich thuoc ma tran: '); readln(sz);
Thủ tục GetMem vẫn dùng được để cấp vùng nhớ cho P như cũ Phép
truy cập các phần tử của mảng trỏ bởi P sẽ như thông thường, không phải ép kiểu Chương trình dễ hiểu hơn Tuy nhiên, ví dụ trên cho thấy tính linh hoạt trong sử dụng con trỏ không định kiểu
4 Vùng ngăn xếp và vùng Heap
4.1 Khái niệm
Vùng ngăn xếp hay Stack lμ vùng nhớ dμnh cho các biến cục bộ của chương trình con Mỗi khi gọi một chương trình con, một stack (kích thước mặc định lμ 16 KB) được tạo ra Khi kết thúc vμ ra khỏi chương trình con, stack nμy được giải phóng
Vùng nhớ dμnh chỗ cho các stack lμ vùng thấp, kế tiếp phần vùng nhớ
đã lưu giữ mã của chương trình