Bài thực hành số 10:
Tổ chức chương trình có cấu trúc
A. Mục tiêu
Tổ chức chương trình có cấu trúc : Cấu trúc về lệnh, cấu trúc về dữ
liệu, cấu trúc về chương trình (modul, hàm. . .)
Vận dụng các nguyên lý lập trình cấu trúc cơ bản: phân rã bài toán
theo chức năng, làm mịn dần từng bước, thiết kế từ trên xuống để tổ
chức chương trình có cấu trúc
Phân biệt phạm vi, tác dụng các loại biến
Tổ chức thư viện chương trình.
B. Ôn tập:
Các nguyên lý lập trình cơ bản
Phạm vi, tác dụng các loại biến : toàn cục, cục bộ, biến tĩnh
Tổ chức Project có tập tin thư viện *.h
I. Phân rã bài toán theo chức năng và tiếp cận từ trên xuống:
1. Các nguyên lý lập trình cơ bản:
• Phân rã theo chức năng:
Dựa vào các chức năng, các yếu tố cấu thành bài toán, ta phân rã bài toán
thành các bài toán con. Lời giải của bài toán đã cho sẽ được xác định từ các
lời giải của các bài toán con.
Lời giải của bài toán có thể xem là chương trình cần viết, tạo ra từ các modul,
hàm (là lời giải của các bài toán con).
• Thiết kế từ trên xuống
Đi từ cái chung đến cái riêng,từ kết luận đến cái đã biết
• Phương pháp làm mịn dần
Làm mịn dần từng bước gắn liền với quá trình phân rã và thiết kế từ trên
xuống, nó chính xác dần thao tác và dữ liệu theo từng mức.
II. Phạm vi, tác dụng các loại biến
1. Biến cục bộ (biến trong):
Vị trí khai báo Thời gian tồn tại Phạm vi tác dụng
-Trong hàm, khối lệnh.
-Đối của hàm.
Trong khoảng thời gian hàm
hay khối lệnh hoạt động
Hàm hay khối lệnh
chứa nó.
Lưu ý:
Khi chương trình ra khỏi khối lệnh hay hàm chứa biến cục bộ thì chúng tự
động mất đi.
2. Biến toàn cục (biến ngoài):
Vị trí khai báo Thời gian tồn tại Phạm vi tác dụng
Ngoài tất cả các hàm. Trong suốt thời gian chương
trình chứa nó hoạt động.
Từ vị trí khai báo đến
cuối tập tin
Lưu ý:
• Mọi hàm đều có thể thâm nhập vào biến toàn cục bằng cách tham trỏ đến tên
của nó.
• Biến toàn cục không tự xuất hiện và tự biến đi, cho nên chúng còn giữ lại giá
trị qua mỗi lầm gọi hàm.
3. Biến tĩnh trong:
Vị trí khai báo Cách khai báo Thời gian tồn tại Phạm vi tác dụng
Trong hàm. Thêm từ khóa static
trước định nghĩa biến
thông thường.
Trong suốt thời
gian chương trình
Bên trong hàm chứa
nó
Lưu ý:
Biến tĩnh trong khác với biến cục bộ ở điểm : Giá trị của biến tỉnh trong vẫn
được lưu giữ khi ra khỏi hàm .
4. Biến tĩnh ngoài:
Vị trí khai báo Cách khai báo Thời gian tồn tại Phạm vi tác dụng
Ngoài tất cả
các hàm.
Thêm từ khóa static
trước định nghĩa biến
thông thường.
Trong suốt thời
gian chương trình
Từ vị trí khai báo
đến cuối tập tin
III. Tổ chức project có tập tin thư viện *h
Ta đã biết cách tổ chức project có một tập tin .cpp : giả sử đã có project với tên
Lab_Vd, với tập tin chương trình là Cpp_Vd.cpp
Ta tao thêm tập tin *.h, đặt tên h_Vd như sau:
Chọn Header Files – Nhấn phím phải chuột – Add – New item – Chọn Code
(trong Categories – Code) – Chọn header file (.h) ( trong Templates) – đặt tên
h_Vd (trong ô Name) – chọn Add.
Trong mỗi tập tin (*.h, *cpp) ta sọan thảo nội dung theo cấu trúc đã qui định.
• Trong tập tin *h, nội dung nên bao gồm các định nghĩa hằng, định nghĩa kiểu
dữ liệu, định nghĩa các hàm chức năng
• Trong tập tin *.cpp, cài đặt hàm main(), có thể có các hàm tổ chức menu, các
hàm nhập xuất dữ liệu.
C. Luyện tập:
Ví dụ 1:
Ta xem tên là một xâu ký tự bao gồm nhiều từ tách biệt bằng các ký tự trắng.
Từ là một dãy liên tiếp các ký tự khác ký tự trắng. Viết chương trình nắn các tên nhập
từ bàn phím theo qui cách:
• Khử các ký tự trắng ở đầu và cuối của tên.
• Khử bớt các dấu cách ở giữa các từ, chỉ để lại một ký tự trắng (khoảng cách).
• Các chữ cái đầu từ được viết hoa, ngoài ra mọi chữ cái còn lại được viết
thường.
Phân tích:
• Tên gồm nhiều từ, các từ phân biệt bằng các ký tự trằng (khoảng cách)
• Vậy bài toán Nắn tên chuyền về Nắn từ.
• Nắn các từ theo qui cách : Đầu từ phải là ký tự hoa, nên phải gọi thao tác chuyển đổi
một ký tự thành ký tự hoa: Hoa(x). Các ký tự còn lại trong Từ phải là ký tự thường,
nên phải gọi thao tác chuyển đổi một ký tự thành ký tự thường: Thuong(x)
• Nếu ta xử lý mỗi từ xong, ta ghi từ đó vào một xầu ký tự tạm, vật ta cần thao tác
ghép từ vào cuối một xâu ký tự.
Nếu ta thực hiện việc xử lý xong một ký tự của từ thì ghép ký tự đó vào xâu ký tự,
thì ta có thể thay thao tác ghép 1 từ vào sau xâu ký tự bằng thao tác ghép một ký tự
vào cuối xâu ký tự: GhepKT(x,a).
Thực hiện:
Bước 1: Tạo Project với tên “Lab10_Vd1”.
Bước 2: Tạo các tập tin: Cpp_Vd1.cpp, h_Vd1.h
Bước 3: Trong các tập tin Cpp_Vd1.cpp, h_Vd1.h, soạn code theo cấu trúc:
• Trong tập tin thư viện h_Vd1.h:
(trong tập tin này không có định nghĩa hàm main)
//Chen cac tap tin thu vien can thiet
#include<string.h>
using namespace std;
//Dinh nghia cac hang
#define CACH ‘ ‘
#define MAX 100
//Dinh nghia kieu du lieu
//Khai bao nguyen mau cac ham
void Nanten(char *a);
char Hoa(char Kt);
char Thuong(char Kt);
void GhepKT(char Kt, char *a);
//Dinh nghia cac ham
//Nan ten
void Nanten(char *a)
{
main()
Nhap(a) NanTen(a
)
Xuat(a)
Thuong(x)Hoa(x) GhepKT(x,a)
char *b;
int i;
b = new char[100];
*b = NULL;
i = 0;
while (*(a+i) == CACH )
i++;
while (*(a+i) != NULL)
{
GhepKT(Hoa(*(a+i)),b);
i++;
while ((*(a+i) != CACH )&& (*(a+i) != NULL))
{
GhepKT(Thuong(*(a+i)),b);
i++;
}
while (*(a+i) == CACH)
i++;
if (a[i] != NULL)
GhepKT(CACH,b);
}
strcpy( a,b);
delete []b;
}
//Ghep ky tu vao cuoi xau
void GhepKT(char Kt, char *a)
{
int l = strlen(a);
*(a+l++) = Kt;
*(a+l) = NULL;
}
//Chuyen ky tu thanh ky tu hoa
char Hoa(char Kt)
{
if ('a' <= Kt && Kt <= 'z')
Kt = Kt - 32;
return Kt;
}
//Chuyen ky tu thanh ky tu thuong
char Thuong(char Kt)
{
if ('A' <= Kt && Kt <= 'Z')
Kt = Kt + 32;
return Kt;
}
• Trong tập tin chương trình Cpp_Vd1.cpp:
//Chen cac tap tin thu vien can thiet, dac biet tap tin <\[duong dan]\h_Vd1.h>
#include<iostream>
#include<stdio.h>
#include<\Lab\Lab10_Vd1\Lab10_Vd1\h_Vd1.h>
//Khai bao nguyen mau cac ham dinh nghia trong tap tin nay
void main()
{
char *a;
a = new char[MAX];
_flushall();
cout<<"\nNhap ten: ";
gets(a);
cout<<"\nTen truoc khi nan: "<<a;
Nanten(a);
cout<<"\nTen sau khi nan: "<<a;
cout<<'\n';
delete []a;
}
//Dinh nghia cac ham
Ví dụ 2:
Thực hiện các thao tác trên dãy n số nguyên với các chức năng sau :
1. Trả về chỉ số của phần tử cuối cùng bằng x nếu có; trả về -1 nếu không có.
2. Tổng các số nguyên tố trong dãy
3. Trả về số đường chạy trong dãy
Các yêu cầu:
• Dãy số: cài đặt mảng động bằng con trỏ
• Chương trình: dạng menu
• Các hàm chức năng: cài đặt bằng thuật toán đệ qui
Thực hiện:
Bước 1: Tạo Project với tên “Lab10_Vd2”.
Bước 2: Tạo các tập tin: Cpp_Vd2.cpp, h_Vd2.h
Bước 3: Trong các tập tin Cpp_Vd2.cpp, h_Vd2.h, soạn code theo cấu trúc:
• Trong tập tin thư viện h_Vd2.h:
(trong tập tin này định nghĩa các hàm chức năng, và không định nghĩa hàm
main)
#include<math.h>
//Khai bao nguyen mau
int Cscc(int *a, int n, int x);
int Tong_nT(int *a, int n);
int So_DC(int *a, int n);
int nt(int x);
//
int Cscc(int *a, int n, int x)
{
int Kq;
if (n == 1)
if (*a == x)
Kq = 0;
else
Kq = -1;
else
if(n > 1)
if(*(a+n-1)==x)
Kq = n-1;
else
Kq = Cscc(a,n-1,x);
return Kq;
}
int Tong_nT(int *a, int n)
{
int Kq;
if ( n==1)
if (nt(*a))
Kq = *a;
else
Kq = 0;
else
if (n >1)
if (nt(*(a+n-1)))
Kq = Tong_nT(a,n-1) + *(a+n-1);
else
Kq = Tong_nT(a,n-1);
return Kq;
}
int So_DC(int *a, int n)
{
int Kq;
if (n == 1)
Kq = 1;
else
if(n > 1)
if(*(a+n-1)<*(a+n-2) )
Kq = So_DC(a,n-1) +1;
else
Kq = So_DC(a,n-1);
return Kq;
}
//Cac ham bo tro
int nt(int x)
{
int Kq, i, m;
double y;
if(x < 2)
Kq = 0;
else
{
Kq = 1;
y = x;
m = (int)sqrt(y);
i = 2;
while (i <= m && Kq)
{
if(x % i == 0)
Kq = 0;
i++;
}
}
return Kq;
}
• Trong tập tin thư viện Cpph_Vd2.cpp:
#include<iostream>
#include<\Lab\Lab10_Vd2\Lab10_Vd2\h_Vd2.h>
#include<stdlib.h>
using namespace std;
void Nhap(int *a, int n);
void Xuat(int *a, int n);
void XL_Menu(int *a, int n, int Chon);
void Menu();
int ChonMenu();
void main()
{
int *a, n, Chon;
cout<<"\nnhap n = ";
cin>>n;
a = new int[n];
Nhap(a,n);
do
{
Chon = ChonMenu();
XL_Menu(a,n, Chon);
}
while(1);
cout<<'\n';
delete []a;
}
void Menu()
{
cout<<"\n BAnG MENU ";
cout<<"\n1. Chi so cua pt cuoi cung bang x";
cout<<"\n2. Tong cac so nguyen to";
cout<<"\n3. So duong chay";
cout<<"\n4. Thoat khoi chuong trinh!!!";
}
int ChonMenu()
{
int Chon;
for(;;)
{
Menu();
cout<<"\nnhap Chon tu 1 -> 4: ";
cin>>Chon;
if (1 <= Chon && Chon <= 4)
break;
}
return Chon;
}
void XL_Menu(int *a, int n, int Chon)
{
int Kq, x;
switch(Chon)
{
case 1:
cout<<"\n1. Chi so cua pt cuoi cung bang x";
Xuat(a, n);
cout<<"\nnhap x = ";
cin>>x;
Kq = Cscc(a,n, x);
if (Kq == -1)
cout<<"\n"<<x<<" khong co trong a!";
else
cout<<"\nchi so cua pt cuoi cung == "<<x<<" la: "<<Kq;
cout<<'\n';
break;
case 2:
cout<<"\n2. Tong cac so nguyen to";
Xuat(a, n);
cout<<"\nTong cac so NT trong a: S = "<<Tong_nT(a,n);
cout<<'\n';
break;
case 3:
cout<<"\n3. So duong chay";
Xuat(a, n);
cout<<"\nSo duong chay trong a: SDC = "<<So_DC(a,n);
cout<<'\n';
break;
case 4:
cout<<"\n9. Thoat khoi CT!\n";
exit(1);
}
}
//Cac ham nhap xuat
void Nhap(int *a, int n)
{
for (int i = 0; i < n; i++)
{
cout<<"\na["<<i<<"] = ";
cin>>*(a+i);
}
}
void Xuat(int *a, int n)
{
int i;
cout<<"\nDay a:\n";
for (i = 0; i < n; i++)
cout<<*(a+i)<<'\t';
}
Ví dụ 3:
Quản lý sinh viên dựa vào các thông tin sau :
• Mã sinh viên
• Họ tên sinh viên
• Lớp
• Điểm trung bình
• Số tín chỉ tích lũy
Viết chương trình khởi tạo dữ liệu sinh viên, và thực hiện các chức năng:
1. Tìm kiếm theo mã sinh viên
2. Thêm sinh viên vào cuối danh sách
3. Xóa sinh viên ra khỏi danh sách theo mã số
4. Xem danh sách sinh viên
Thực hiện:
Bước 1: Tạo Project với tên “Lab10_Vd3”.
Bước 2: Tạo các tập tin: Cpp_Vd3.cpp, h_Vd3.h
Bước 3: Trong các tập tin Cpp_Vd3.cpp, h_Vd2.h, soạn code theo cấu trúc:
• Trong tập tin thư viện h_Vd3.h:
#include<iostream>
#include<string.h>
#include<iomanip>
#include<stdio.h>
#include<conio.h>
#define MAX 1000
using namespace std;
//Dinh nghia kieu du lieu
struct SINHVIEN
{
char *Maso;
char *HoTen;
char *Lop;
double Dtb;
int Tichluy;
};
//Cac bien toan cuc
SINHVIEN Sv[MAX];
int n = 0;
//Khai bao nguyen mau
void Output();
void Output_Data();
void Output_Struct(SINHVIEN p);
void Setup();
void Chen_Ct(char *Maso,char *HoTen,char *Lop,double Dtb, int Tichluy);
void Xoa_Ct(char *Maso);
int Tim_Maso (char *Maso);
//Khoi tao danh sach sinh vien
void Setup()
{
Chen_Ct("0213345","Nguyen Van","CTK100",4.5,41);
Chen_Ct("0210340","Truong Van A","CTK101",8.0,39);
Chen_Ct("0201381","Duong Mai","CTK100",4.5,38);
Chen_Ct("0211348","Tran Vuong","CTK100",5,34);
Chen_Ct("0210042","Hoang Trong","CTK101",6,38);
Chen_Ct("0201380","Duong Mai","CTK100",6,36);
Chen_Ct("0201180","Tran Hoan","CTK100",5,37);
Chen_Ct("0211380","Luu Trong","CTK100",6.5,36);
}
//Chen mot sinh vien vao danh sach Sinh vien
void Chen_Ct(char *Maso,char *HoTen,char *Lop,double Dtb, int Tichluy)
{
if (n < MAX)
{
flushall();
Sv[n].Maso = new char[12];
strcpy(Sv[n].Maso, Maso);
flushall();
Sv[n].HoTen = new char[20];
strcpy(Sv[n].HoTen,HoTen);
flushall();
Sv[n].Lop = new char[10];
strcpy(Sv[n].Lop,Lop);
Sv[n].Dtb = Dtb;
Sv[n].Tichluy = Tichluy;
}
n++;
}
//Xoa sinh vien theo ma so
void Xoa_Ct(char *Maso)
{
int i, Kq = Tim_Maso (Maso);
if (n == 0)
{
cout<<"\nDS rong! khong xoa duoc!";
_getch();
return;
}
if (Kq == -1)
{
cout<<"\nKhong co sinh vien nao co ma so "<<Maso;
_getch();
return;
}
cout<<"\nThong tin sinh vien bi xoa:\n";
Output_Struct(Sv[Kq]);
for (i = Kq +1; i < n; i++)
Sv[i-1] = Sv[i];
n ;
}
//xuat du lieu 1 cau truc (sinh vien)
void Output_Struct(SINHVIEN p)
{
cout<<'\n';
cout <<setiosflags(ios::left)
<<setw(12)<<p.Maso
<<setw(22)<<p.HoTen
<<setw(10)<<p.Lop
<<setw(10)<<p.Dtb
<<setw(10)<<p.Tichluy;
}
//Xuat danh sach nhan vien ra man hinh
void Output_Data()
{
for (int i = 0; i < n; i++)
Output_Struct(Sv[i]);
}
//In tieu de
void Output()
{
cout<<'\n';
cout <<setiosflags(ios::left)
<<setw(12)<<"MASO"
<<setw(22)<<"HOTEN"
<<setw(10)<<"LOP"
<<setw(10)<<"DTB"
<<setw(10)<<"TICHLUY";
}
//Tim theo ma so: tra ve chi so i sao cho sv[i].maso = Maso neu co
int Tim_Maso (char *Maso)
{
int i=0;
while ((i < n) && (stricmp(Sv[i].Maso, Maso)))
i++;
if (i == n)
return -1; //khong co
return i;
}
• Trong tập tin chương trình Cpp_Vd3.cpp:
//Chen tap tin thu vien
#include<\Lab\Lab10_Vd3\Lab10_Vd3\h_Vd3.h>
//Khai bao nguyen mau
void Menu();
int ChonMenu();
void XL_Menu(int Chon);
void main()
{
int Chon;
Setup();
do
{
Chon = ChonMenu();
XL_Menu(Chon);
}
while(1);
}
//Xuat ten cac chuc nang cua CT ra man hinh
void Menu()
{
cout<<"\n MENU ";
cout<<"\n1.Tim theo ma so";
cout<<"\n2.Them sinh vien";
cout<<"\n3.Xoa sinh vien";
cout<<"\n4.Xem danh sach";
cout<<"\n5.Thoat khoi chuong trinh!!!";
}
//Dieu khien viec chon chuc nang cua nguoi SD
int ChonMenu()
{
int Chon;
for(;;)
{
Menu();
cout<<"\nNhap gia tri chon chuc nang (1-5): ";
cin>>Chon;
if(Chon >= 1 && Chon <= 5)
break;
}
return Chon;
}
//Xu ly CT
void XL_Menu(int Chon)
{
char *Maso, *HoTen, *Lop;
double Dtb;
int Tichluy;
int Kq;
int Cd, Ct;
switch(Chon)
{
case 1:
cout<<"\n1.Tim theo ma so:\n";
cout<<"\nNhap ma so: ";
flushall();
Maso = new char[12];
gets(Maso);
cout<<"\n Danh sach sinh vien \n";
Output();
Output_Data();
cout<<"\n\nKet qua tim:\n";
Kq = Tim_Maso (Maso);
if(Kq == -1)
cout<<"\nKhong co sinh vien nay!";
else
cout<<"\nSinh vien thu "<<Kq<<" co ma so nay";
cout<<"\n";
delete []Maso;
break;
case 2:
cout<<"\n2.Them sinh vien";
cout<<"\n Danh sach sinh vien ban dau \n";
Output();
Output_Data();
Maso = new char[12];
HoTen = new char[20];
Lop = new char[10];
cout<<"\nNhap ma so SV: ";
flushall();
gets(Maso);
cout<<"\nNhap Ho Ten SV: ";
flushall();
gets(HoTen);
cout<<"\nNhap ma so lop: ";
flushall();
cin>>Lop;
flushall();
cout<<"\nDiem trung binh: ";
cin>>Dtb;
cout<<"\nTich luy: ";
cin>>Tichluy;
Chen_Ct(Maso,HoTen,Lop,Dtb, Tichluy);
cout<<"\n Danh sach sinh vien ket qua \n";
Output();
Output_Data();
cout<<"\n";
delete []Maso;
delete []HoTen;
delete []Lop;
break;
case 3:
cout<<"\n3.Xoa sinh vien";
cout<<"\n Danh sach sinh vien ban dau \n";
Output();
Output_Data();
cout<<"\nNhap ma so SV: ";
flushall();
Maso = new char[12];
gets(Maso);
Xoa_Ct(Maso);
cout<<"\n Danh sach sinh vien ket qua \n";
Output();
Output_Data();
cout<<"\n";
delete []Maso;
break;
case 4:
cout<<"\n4.Xem Danh sach:\n";
cout<<"\n Danh sach sinh vien \n";
Output();
Output_Data();
cout<<"\n";
break;
case 5:
cout<<"\n5.Thoat khoi chuong trinh!!!\n";
exit(1);
}
}
Lưu ý:
• Các biến toàn cục:
SINHVIEN Sv[MAX]; //dữ liệu sinh viên
int n = 0;//n là số lượng sinh viên, khởi đầu bằng 0
• Khởi tạo dữ liệu: bằng hàm Setup()
• Các hàm có thể thay đổi giá trị các biến toàn cục: n, Sv.
D. Bài tập
Bài 1:
Tổ chức chương trình menu thực hiện các thao tác trên phân số :
• Cộng phân số.
• Trừ phân số.
• Nhân phân số.
• Chia phân số.
Bài 2:
Xác định đường tròn đi qua 4 điểm cho trước ?
Nếu có, xuất đường tròn tương ứng; nguợc lại thông báo không có.
Viết chương trình giải quyết bài toán trên.
Phân tích:
• Nếu có 3 điểm thẳng hàng, thông báo không có đường tròn qua 4 điểm, kết
thúc
• Tạo đường tròn qua 3 điểm không thẳng hàng.
• Kiểm tra điểm còn lại có thuộc đường tròn đã tạo hay không ?
Nếu không, thông báo không có đường tròn đi qua 4 điểm ?
Nếu có xuất đường tròn (tâm & bán kính)
Hướng dẫn:
1. Tổ chức dữ liệu
struct DIEM
{
double x;
double y;
};
struct DUONG_TRON
{
DIEM Tam;
double R; //Ban kinh binh phuong
};
2. Các chức năng
a. Nhập 4 điểm (lưu trử trong mảng 1 chiểu kiểu cấu trúc DIEM
void Nhap(DIEM M[4]);
b. Xuất đường tròn kết quả (Tâm, bán kính)
void Xuat(DUONG_TRON Dt);
c. Tạo đường tròn từ 3 điểm không thảng hàng
int Taodt(DIEM A, DIEM B, DIEM C, DUONG_TRON &Dt);
d. Kiểm tra 3 điểm có thẳng hàng hay không?
int Thang_hang(DIEM A, DIEM B, DIEM C);
e. Kiểm tra điểm A có nằm trên đường tròn Dt hay không?
int Thuoc(DIEM A, DUONG_TRON Dt);
f. Xác định đường tròn từ 4 điểm
int Xddt(DIEM M[4], DUONG_TRON &Dt);
g. Xác định tâm đường tròn đi qua 3 điểm không thẳng hàng
DIEM Tam_dt(double a11,double a12,double b1,double a21, double a22, double b2);
main()
Nhap(M) Xddt(M,Dt) Xuat(Dt)
Tdt(A,B,C,Dt) Thuoc(A,Dt)
Thang_hang(A,B,
C)
Tam_dt(a1,a2,a3,a4,a5,a6)