290 likes | 467 Views
CÂU HỎI : Cho biết kết quả thực thi chương trình sau:. void f(int, int&, int); void main() { int a=100, b=200, c=300; cout<<a<<“ “<<b<<“ “<<c<<endl; f(a,b,c); cout<<a<<“ “<<b<<“ “<<c<<endl; getch(); } void f(int a, int&b, int c) { if (a<b) { a=a+50; b=b-50; }
E N D
CÂU HỎI: Cho biết kết quả thực thi chương trình sau: void f(int, int&, int); void main() { int a=100, b=200, c=300; cout<<a<<“ “<<b<<“ “<<c<<endl; f(a,b,c); cout<<a<<“ “<<b<<“ “<<c<<endl; getch(); } void f(int a, int&b, int c) { if (a<b) { a=a+50; b=b-50; } cout<<a<<“ “<<b<<“ “<<c<<endl; }
CHÖÔNG 5 CHƯƠNG TRÌNH CON (TIẾP THEO) VI. PHẠM VI - THỜI GIAN SỐNG CỦA BIẾN (SCOPE & LIFE TIME) VII. HÀM ĐỆ QUI
VI. PHẠM VI - THỜI GIAN SỐNG CỦA BIẾN (SCOPE & LIFE TIME) • Định nghĩa phạm vi biến: Phạm vi của biến là phần chương trình trong đó việc tham khảo đến biến là hợp lệ. Gồm biến cục bộ và biến tòan cục. • ĐN Thời gian sống của biến: Khi thực thi chương trình, biến được tạo ra và hủy đi. Thời gian sống là thời gian tồn tại của biến từ lúc được tạo ra đến lúc hủy đi. Thời gian sống phụ thuộc phạm vi và lớp lưu trữ.
VI. PHẠM VI - THỜI GIAN SỐNG CỦA BIẾN (SCOPE & LIFE TIME) 1. Biến tòan cục: • Khai báo bên ngoài các hàm. • Tự động khởi tạo giá trị 0 (không cho phép viết lệnh khai báo khởi tạo trị ). • Có thời gian sống cùng với thời gian chạy chương trình. Nhắc lại: int n; //khai báo biến int a=5; //khai báo biến có khởi tạo trị. //không cho phép đ/v tòan cục
VI. PHẠM VI - THỜI GIAN SỐNG CỦA BIẾN (SCOPE & LIFE TIME) 2. Biến cục bộ: • Khai báo bên trong hàm, trong khối lệnh. • Không được tự động khởi tạo giá trị. • Có phạm vi từ vị trí khai báo đến cuối hàm hoặc cuối khối lệnh. • Thời gian sống từ lúc hàm được thực thi hoặc khối lệnh được thực thi đến lúc kết thúc hàm hoặc chạy xong khối lệnh. • Xét ví dụ sau:
n3=4412 main: n1=0 n2=2 sub: n1=0 n2=30 main: n1=40 n2=2 int n1; void sub_fun(); void main() { int n2=2; int n3; cout<<“n3=“<<n3<<endl; cout<<“main: n1=”<<n1<<“n2= “<<n2<<endl; sub_fun(); cout<<“main: n1=”<<n1<<“ n2= “<<n2<<endl; } void sub_fun() { int n2; n2=30; cout<<“sub: n1=”<<n1<<“ n2=“<<n2<<endl; n1=40; }
VI. PHẠM VI - THỜI GIAN SỐNG CỦA BIẾN (SCOPE & LIFE TIME) Lưu ý 1: • Trong cùng phạm vi, mỗi biến chỉ được khai báo một lần. • Phạm vi cục bộ che phủ phạm vi toàn cục. • Trong phạm vi cục bộ, muốn sử dụng biến tòan cục cùng tên ta dùng tóan tử phạm vi (::) • Ví dụ:
main: n1=10 Global: n1=0 Global: n1=40 #include <iostream.h> int n1; void sub_fun(); void main() { int n1=10; cout<<“main: n1=”<<n1<<endl; cout<<“Global: “<<“n1= “<<::n1<<endl; ::n1=40; sub_fun(); } void sub_fun() { cout<<“Global: n1=”<<n1<<endl; }
VI. PHẠM VI - THỜI GIAN SỐNG CỦA BIẾN (SCOPE & LIFE TIME) Lưu ý 2: • Các hàm phải được khai báo toàn cục (Bên trong hàm không được khai báo hàm) • Các hàm cùng tên, khác thông số được gọi là overload. • Ví dụ:
#include <iostream.h> #include <conio.h> void GiaiPhuongTrinh(float, float); void GiaiPhuongTrinh(float,float,float); void main() { float m=2,n=3,p=-4; cout<<“Giai Phuong trinh bac 1: ” <<endl; GiaiPhuongTrinh(m,n); cout<<“Giai Phuong trinh bac 2: ” <<endl; GiaiPhuongTrinh(m,n,p); getch(); }
void GiaiPhuongTrinh(float a, float b) { if (a!=0) cout<<“x=“<<–b/a<<endl; else if (b!=0) cout<<“PTVN”<<endl; else cout<<“VSN”<<endl; return; }
void GiaiPhuongTrinh(float a, float b, float c) { if (a==0) { GiaiPhuongTrinh(a,b); return;} float d=b*b -4*a*c; if (d>0) { cout<<“x1=“<<(-b-sqrt(d))/(2*a)<<endl; cout<<“x2=“<<(-b+sqrt(d))/(2*a)<<endl; } else if (d==0) cout<<“x1=x2=“<<-b/(2*a)<<endl; else cout<<“PTB2 vo nghiem”; return; }
VI. PHẠM VI - THỜI GIAN SỐNG CỦA BIẾN (SCOPE & LIFE TIME) 3. Lớp lưu trữ biến: Có 4 lớp lưu trữ là: auto, static, extern, register. a. Lớp lưu trữ cho biến cục bộ: auto, static hoặc register. • Chỉ định lớp khi khai báo một biến cục bộ Ví dụ: static int x; register float y; • Mặc định là auto, không được khởi động trị. Ví dụ: int n; //n có một trị bất kỳ khi tạo • Biến thuộc lớp register có đặc tính giống auto, nhưng được lưu trong thanh ghi (Vùng nhớ CPU).
VI. PHẠM VI - THỜI GIAN SỐNG CỦA BIẾN (SCOPE & LIFE TIME) • Biến cục bộ auto và register có thời gian sống cùng với hàm nên trị mất đi sau mỗi lần gọi hàm. • Biến cục bộ static có thời gian sống cùng với chương trình. Được khởi động trị tự động nếu ta không khởi động trị cho nó. Và chỉ khởi động trị 1 lần duy nhất khi được tạo. Nên giữ lại được giá trị của lần gọi hàm trước đó. • Ví dụ
1 • 1 • 1 • 1 • 5 1 #include <iostream.h> #include <conio.h> void sub(); void main() { int i; for (i=0; i<5;i++) sub(); return; } void sub() { static int num1=1; int num2=1; cout <<num1<<" “<<num2<<endl; num1++; num2++; return; }
VI. PHẠM VI - THỜI GIAN SỐNG CỦA BIẾN (SCOPE & LIFE TIME) b. Lớp lưu trữ cho biến tòan cục: extern hoặc static. • Ví dụ: extern int n; static float b; • Lớp lưu trữ của biến tòan cục chỉ để thay đổi phạm vi sử dụng biến, không đổi thời gian sống: • extern để thông báo (declaration) một biến đã được khai báo (definition) ở tập tin khác. • static để ngăn cấm không cho biến sử dụng ở tập tin khác.
VII. HÀM ĐỆ QUI 1. Định nghĩa: Hàm đệ qui là hàm có chứa (hoặc ẩn chứa) lời gọi hàm lại chính nó. Ví dụ: Hàm in ra n dấu * void InDauSao(int n) { cout<<‘*’; if (n>1) InDauSao(n-1); }
VII. HÀM ĐỆ QUI 2. Phân lọai: • Đệ qui tuyến tính: Trong hàm chỉ có 1 lời gọi hàm đệ qui. • Đệ qui phi tuyến: Có hơn 1 lời gọi hàm đệ qui. Đệ qui nhị phân là trường hợp đặc biệt của đệ qui phi tuyến. • Đệ qui tương hỗ: Không đệ qui trực tiếp, không gọi lại chính mình. Ví dụ: A gọi B, B gọi C, C gọi A
VII. HÀM ĐỆ QUI 3. Sử dụng hàm đệ qui khi nào? • Giải bài tóan có tính đệ qui giúp chương trình ngắn gọn. • Là dạng tóan có thể qui về một bài tóan cùng dạng nhưng với số lượng phép tóan hay công việc giảm đi. • Ví dụ:
VII. HÀM ĐỆ QUI 3. Sử dụng hàm đệ qui khi nào? • Ví dụ: n! = n * (n-1)! USC(x,y)= USC(x-y,y) nếu x>y Số chữ số của x= 1 + số chữ số của x/10 fibonacci(n)=fibonacci(n-1)+fibonacci(n-2)
VII. HÀM ĐỆ QUI 4. Nguyên tắc viết hàm đệ qui: Hàm đệ qui cần thỏa 2 đk: • Giải quyết được trong trường hợp riêng ứng với giá trị đặc biệt của tham số (tr/h suy biến). • Trường hợp tổng quát, bài tóan được định nghĩa đệ qui với giá trị tham số thay đổi. Ví dụ: Tính n! Tổng quát: n!= n*(n-1)! Suy biến: Bằng 1 khi n=0||n=1
VII. HÀM ĐỆ QUI • Như vậy một hàm đệ qui sẽ có dạng ... hàmF (...) { if ( thông_số == trị_đặc_biệt ) return kết_quả; .... hàmF (...); .... }
5. Quá trình thực thi hàm đệ qui: Ví dụ 1: Đếm số chữ số của một số nguyên n>0 int DigitCount(int n) { if (n<10) return 1; else return 1+DigitCount(n/10); } Gọi hàm DigitCount(517) ta có quá trình thực thi sau:
Thực thi DigitCount(517) Khởi tạo biến cục bộ và tham trị n=517 { if (n<10) //false return 1+DigitCount(51); } Thực thi DigitCount(51) Khởi tạo biến cục bộ và tham trị n=51 { if (n<10) //false return 1+ DigitCount(5); } Thực thi DigitCount(5) Khởi tạo biến cục bộ và tham trị n=5 { if (n<10) //true return 1; } 5. Quá trình thực thi hàm đệ qui: 3 DigitCount(517) 2 1
5. Quá trình thực thi hàm đệ qui: Ví dụ 2: Tìm ước số chung lớn nhất của 2 số nguyên dương. int USCLN(int u, int v) { if (v==0) return u; else return USCLN(v, u%v); } Gọi hàm USCLN(9,12) ta có quá trình thực thi sau:
Thực thi USCLN(9,6) Khởi tạo biến cục bộ và tham trị u=9, v=6 { if (v=0) //false return USCLN(6,3); } Thực thi USCLN(6,3) Khởi tạo biến cục bộ và tham trị u=6, v=3 { if (v==0) //false return USCLN(3,0); } Thực thi USCLN(3,0) Khởi tạo biến cục bộ và tham trị u=3, v=0 { if (v==0) //true return u; } 5. Quá trình thực thi hàm đệ qui: 3 USCLN(9,6) 3 3 3
5. Quá trình thực thi hàm đệ qui: Kết luận: • Mỗi lần gọi đệ qui tập các biến cục bộ, tham trị được tạo ra độc lập với tập các biến tạo ra cho lần gọi hàm trước. • Mỗi lần thóat ra khỏi hàm của 1 lần gọi thì tập biến cục bộ tương ứng bị xóa. • Lần gọi hàm sau cùng sẽ thóat ra đầu tiên. • Hàm đệ qui thực thi cần nhiều không gian nhớ hơn hàm không đệ qui.
6. Các ví dụ khác • S= 1 + 2 + 3 + .... + n • S= x + x3+ x5 + ... + x2n+1 • Tìm USCLN của 2 số bằng thuật tóan trừ • Tính số Fibonacci thứ n • Bài tóan tháp hà nội