Ngôn ngữ lập trình - Bài 3: Hàm và Nạp chồng hàm

pdf 57 trang vanle 15/06/2021 15500
Bạn đang xem 20 trang mẫu của tài liệu "Ngôn ngữ lập trình - Bài 3: Hàm và Nạp chồng hàm", để tải tài liệu gốc về máy bạn click vào nút DOWNLOAD ở trên

Tài liệu đính kèm:

  • pdfngon_ngu_lap_trinh_bai_3_ham_va_nap_chong_ham.pdf

Nội dung text: Ngôn ngữ lập trình - Bài 3: Hàm và Nạp chồng hàm

  1. NGÔN NGỮ LẬP TRÌNH Bài 3: Hàm và Nạp chồng hàm Giảng viên: Lý Anh Tu ấn Email: tuanla@tlu.edu.vn
  2. Nội dung 1. Hàm định nghĩa trước ◦ Hàm trả về giá trị và hàm không trả về giá trị 2. Hàm người dùng định nghĩa ◦ Định nghĩa, khai báo, gọi hàm 3. Phạm vi ◦ Biến cục bộ ◦ Hằng và biến toàn cục 4. Tham số ◦ Truyền giá trị ◦ Truyền tham biến 5. Nạp chồng và tham số mặc định 2
  3. Giới thiệu hàm  Xây dựng các khối cho chương trình  Cách gọi trong các ngôn ngữ khác ◦ Thủ tục, chương trình con, phương thức ◦ Trong C++: hàm  I-P-O ◦ Đầu vào – Xử lý – Đầu ra ◦ Là các thành phần cơ bản của mỗi chương trình ◦ Sử dụng hàm cho mỗi thành phần này 3
  4. Hàm định nghĩa trước  Trong các thư viện có sẵn rất nhiều hàm  Hai kiểu hàm: ◦ Hàm trả về giá trị ◦ Hàm không trả về giá trị (void)  Phải “#include” thư viện phù hợp ◦ Ví dụ:  , (các thư viện của “C”)  (dùng cho cout, cin) 4
  5. Hàm định nghĩa trước  Có rất nhiều hàm toán học ◦ Nằm trong thư viện ◦ Hầu hết trả về một giá trị (câu trả lời)  Ví dụ: theRoot = sqrt(9.0); ◦ Các thành phần: sqrt = tên của hàm thư viện theRoot = biến được sử dụng để nhận câu trả lời 9.0 = đối số hoặc “khởi tạo đầu vào” của hàm 5
  6. Lời gọi hàm  Xét lệnh gán: theRoot = sqrt(9.0); ◦ Biểu thức “sqrt(9.0)” được hiểu như là một lời gọi hàm ◦ Đối số trong lời gọi hàm (9.0) có thể là một literal, một biến, hoặc một biểu thức  Lời gọi có thể là một phần của biểu thức: ◦ VD: bonus = sqrt(sales)/10; ◦ Dựa vào kiểu trả về của hàm để biết nơi được phép sử dụng lời hàm 6
  7. Ví dụ: Hàm định nghĩa trước 7
  8. Ví dụ: Hàm định nghĩa trước 8
  9. Một số hàm định nghĩa trước  #include , thư viện gồm các hàm: ◦ abs() // Trả về giá trị tuyệt đối của một số int ◦ labs() // Trả về giá trị tuyệt đối của một số long int ◦ fabs() // Trả về giá trị tuyệt đối của một số float  Hàm pow(x, y): Trả về x mũ y ◦ VD: Cho biết kết quả in ra của đoạn mã lệnh double result, x = 3.0, y = 2.0; result = pow(x, y); cout << result; 9
  10. Một số hàm toán học 10
  11. Một số hàm toán học 11
  12. Hàm void định nghĩa trước  Không trả về giá trị  Thực hiện một hành động, nhưng không gửi câu trả lời  Khi được gọi, bản thân nó là một câu lệnh ◦ VD: exit(1); //Không trả về giá trị, do vậy không được sử dụng để gán  Các khía cạnh khác tương tự như hàm trả về giá trị 12
  13. Bộ phát sinh số ngẫu nhiên  Trả về số “được chọn ngẫu nhiên”  Sử dụng trong mô phỏng, trò chơi ◦ rand(): không có tham số, trả về giá trị giữa 0 & RAND_MAX ◦ Thu hẹp phạm vi: rand() % 6: trả về số ngẫu nhiên giữa 0 & 5 ◦ Tịnh tiến: rand() % 6 +1: dịch chuyển giữa 1 & 6 13
  14. Hạt giống số ngẫu nhiên  Các số giả ngẫu nhiên ◦ Gọi rand() tạo ra một chuỗi các số ngẫu nhiên  Sử dụng “hạt giống” để sửa đổi chuỗi srand(seed_value); ◦ Là hàm void có một đối số ◦ Có thể sử dụng bất cứ giá trị hạt giống nào,  VD: srand(time(0)); ◦ time() trả về thời gian hệ thống ◦ time() nằm trong thư viện 14
  15. Các ví dụ ngẫu nhiên  Số thực ngẫu nhiên giữa 0.0 & 1.0: (RAND_MAX – rand())/static_cast (RAND_MAX) ◦ Ép kiểu cho phép chia số thực  Số nguyên ngẫu nhiên giữa 1 & 6: rand() % 6 + 1 ◦ “%” là toán tử chia lấy phần dư  Số nguyên ngẫu nhiên giữa 10 & 20: rand() % 11 + 10 15
  16. Hàm người dùng định nghĩa  Cho phép tự viết hàm của riêng mình  Xây dựng các khối chương trình ◦ Chia để trị ◦ Khả đọc ◦ Sử dụng lại  Định nghĩa hàm có thể nằm: ◦ Cùng file với hàm main(), hoặc ◦ Trong file riêng rẽ để những người khác cũng có thể sử dụng 16
  17. Các thành phần của hàm  Khai báo hàm/nguyên mẫu hàm ◦ Thông tin cho trình biên dịch ◦ Thông dịch chính xác lời gọi  Định nghĩa hàm ◦ Sự thực thi hay mã lệnh thực hiện công việc của hàm  Lời gọi hàm ◦ Chuyển điều khiển cho hàm 17
  18. Khai báo hàm  Còn được gọi là nguyên mẫu hàm  Bộ biên dịch dựa vào nó để thông dịch lời gọi ◦ Cú pháp: FnName( ); ◦ Ví dụ: double totalCost( int numberParameter, double priceParameter);  Được đặt trước bất kỳ lời gọi nào ◦ Trong không gian khai báo của hàm main() ◦ Hoặc trong không gian toàn cục trước hàm main() 18
  19. Định nghĩa hàm  Sự thực thi của hàm, giống như sự thi hàm main()  Ví dụ: double totalCost( int numberParameter, double priceParameter) { const double TAXRATE = 0.05; double subTotal; subtotal = priceParameter * numberParameter; return (subtotal + subtotal * TAXRATE); }  Lưu ý thụt vào đầu dòng chuẩn 19
  20. Vị trí đặt định nghĩa hàm  Đặt sau hàm main(), không nằm bên trong hàm main()  Các hàm là bình đẳng, không hàm nào là thành phần của hàm khác  Các tham số hình thức trong định nghĩa ◦ Giữ chỗ cho dữ liệu gửi vào ◦ Sử dụng tên biến để tham chiếu tới dữ liệu trong định nghĩa  Lệnh return ◦ Trả dữ liệu về cho lời gọi 20
  21. Lời gọi hàm  Giống lời gọi hàm định nghĩa trước bill = totalCost(number, price);  totalCost trả về giá trị kiểu double, giá trị này được gán cho biến bill  Các đối số: number, price ◦ Đối số có thể là literal, biến, biểu thức, hoặc sự kết hợp của chúng ◦ Trong lời gọi hàm, đối số thường được gọi là “đối số thực sự” 21
  22. Ví dụ hàm 22
  23. Ví dụ hàm 23
  24. Khai báo hàm thay thế  Khai báo hàm cung cấp thông tin cho bộ biên dịch  Bộ biên dịch chỉ cần biết: ◦ Giá trị trả về ◦ Tên hàm ◦ Danh sách tham số  Không cần tên tham số hình thức: double totalCost(int, double);  Tuy nhiên vẫn nên đưa vào cho dễ đọc 24
  25. Hàm gọi hàm  Đã làm việc này rồi: do main() là một hàm  Khai báo hàm phải xuất hiện trước lời gọi hàm  Định nghĩa hàm thường nằm: ◦ Sau định nghĩa hàm main(), hoặc ◦ Trong file riêng rẽ  Hàm có thể gọi đến chính nó “Đệ quy” 25
  26. Hàm trả về kiểu bool  Kiểu trả về có thể là bất kỳ kiểu dữ liệu nào  Một khai báo hàm/nguyên mẫu hàm: bool appropriate(int rate);  Định nghĩa hàm tương ứng: bool appropriate (int rate) { return (((rate>=10)&&(rate<20))||(rate==0); }  Trả về “true” hoặc “false”  Lời gọi hàm, từ một hàm khác: if (appropriate(entered_rate)) cout << "Rate is valid\n"; 26
  27. Khai báo hàm void  Tương tự như hàm trả về giá trị  Kiểu trả về là “void”  VD: Khai báo hàm/nguyên mẫu hàm: void showResults( double fDegrees, double cDegrees);  Kiểu trả về là “void”, nghĩa là không trả về gì 27
  28. Khai báo hàm void  Định nghĩa hàm: void showResults(double fDegrees, double cDegrees) { cout.setf(ios::fixed); cout.setf(ios::showpoint); cout.precision(1); cout << fDegrees << " degrees fahrenheit equals \n" << cDegrees << " degrees celsius.\n"; }  Lưu ý: Không có câu lệnh return 28
  29. Gọi hàm void  Giống như gọi hàm void định nghĩa trước  Gọi từ một hàm khác, chẳng hạn main(): showResults(degreesF, degreesC); showResults(32.5, 0.3);  Không sử dụng để gán, vì không có giá trị trả về  Các đối số thực sự (degreesF, degreesC) ◦ Được truyền cho hàm ◦ Hàm được gọi để thực hiện công việc với dữ liệu được truyền 29
  30. Câu lệnh return  Chuyển điều khiển về cho lời gọi hàm ◦ Phải có câu lệnh return nếu kiểu trả về khác void ◦ Thường là câu lệnh cuối cùng trong định nghĩa hàm  Với hàm void, câu lệnh return là tùy chọn ◦ Dấu } sẽ chuyển điều khiển từ hàm void 30
  31. Hàm main()  main() là một hàm  Chỉ tồn tại duy nhất một hàm main() trong một chương trình  Hàm main() được gọi bởi hệ điều hành  Nó thường có câu lệnh return ◦ Giá trị được trả về cho hệ điều hành  Nên trả về “int” hoặc “void” 31
  32. Phạm vi  Biến cục bộ ◦ Được khai báo bên trong thân của một hàm ◦ Chỉ tồn tại trong hàm đó  Có thể khai báo các biến có cùng tên trong các hàm khác nhau ◦ Phạm vi cục bộ: hàm là phạm vi của nó  Lợi ích của biến cục bộ ◦ Duy trì kiểm soát riêng rẽ với dữ liệu ◦ Hàm nên khai báo bất kỳ dữ liệu cục bộ nào mà nó cần 32
  33. Hằng toàn cục và biến toàn cục  Được khai báo bên ngoài thân hàm ◦ Toàn cục với tất cả các hàm trong file  Khai báo toàn cục cho hằng: ◦ const double TAXRATE = 0.05; ◦ Khai báo là toàn cục do vậy có phạm vi với tất cả các hàm  Biến toàn cục ◦ Được phép nhưng hiếm khi sử dụng ◦ Khó kiểm soát khi sử dụng 33
  34. Khối  Khai báo dữ liệu bên trong lệnh kép ◦ Lệnh kép được gọi là một “khối” và có “phạm vi khối”  Các định nghĩa hàm đều là khối ◦ Tạo ra “phạm vi hàm” cục bộ  Khối lặp: for (int ctr=0;ctr<10;ctr++) { sum+=ctr; } ◦ Biến crt chỉ có phạm vi trong khối thân vòng lặp  Các biến có cùng tên được phép khai báo trong nhiều khối 34
  35. Các tham số  Hai phương pháp truyền đối số làm tham số  Truyền giá trị ◦ Truyền bản sao của giá trị  Truyền tham chiếu ◦ Truyền “địa chỉ” của đối số thực sự 35
  36. Tham số truyền giá trị  Bản sao của đối số thực sự được truyền  Là “biến cục bộ” bên trong hàm  Nếu sửa đổi, chỉ “bản sao cục bộ” thay đổi ◦ Hàm không truy cập tới “đối số thực sự” từ lời gọi  Đây là phương pháp mặc định ◦ Được sử dụng trong tất cả các ví dụ phía trước 36
  37. Ví dụ truyền giá trị 37
  38. Ví dụ truyền giá trị 38
  39. Ví dụ truyền giá trị 39
  40. Lỗi thường gặp khi truyền giá trị  Khai báo lại tham số bên trong hàm double fee(int hoursWorked, int minutesWorked) { int quarterHours; // local variable int minutesWorked // NO! } ◦ Bộ biên dịch sẽ báo lỗi khai báo lại  Đối số giá trị giống như “biến cục bộ”, nhưng hàm tự động có nó 40
  41. Tham số truyền tham chiếu  Cung cấp truy cập đến đối số thực sự của lời gọi  Dữ liệu của lời gọi có thể được sửa đổi bởi hàm được gọi  Thường được sử dụng cho hàm đầu vào ◦ Để lấy dữ liệu và đưa cho lời gọi  Xác định bằng dấu &, sau khi nhập danh sách tham số hình thức  Tham chiếu quay về đối số thực sự của lời gọi ◦ Trỏ đến vùng nhớ của đối số thực sự ◦ Được gọi là “địa chỉ”, là một số duy nhất trỏ đến một vùng riêng biệt của bộ nhớ 41
  42. Ví dụ truyền tham chiếu 42
  43. Ví dụ truyền tham chiếu 43
  44. Ví dụ truyền tham chiếu 44
  45. Tham số tham chiếu hằng  Sử dụng đối số tham chiếu có nhiều rủi ro ◦ Dữ liệu của lời gọi có thể bị thay đổi ◦ Đôi khi chúng ta không mong muốn điều này  Để bảo vệ dữ liệu, và vẫn truyền tham chiếu ◦ Sử dụng từ khóa const void sendConstRef( const int &par1, const int &par2); ◦ Tạo đối số “chỉ đọc” bởi hàm ◦ Không cho phép thay đổi bên trong thân hàm 45
  46. Danh sách tham số trộn  Kết hợp các kỹ thuật truyền, bao gồm các tham số truyền giá trị và truyền tham biến  Trật tự của các đối số trong danh sách là rất quan trọng: void mixedCall(int & par1, int par2, double & par3);  Lời gọi hàm: mixedCall(arg1, arg2, arg3); ◦ arg1 là kiểu nguyên, được truyền theo tham chiếu ◦ arg2 là kiểu nguyên, được truyền theo giá trị ◦ arg3 là kiểu thực, được truyền theo tham chiếu 46
  47. Nạp chồng  Tên hàm giống nhau, danh sách các tham số khác nhau  Hai định nghĩa hàm riêng biệt  Tín hiệu hàm ◦ Tên hàm & danh sách tham số ◦ Phải là “duy nhất” cho mỗi định nghĩa hàm  Cho phép thực hiện cùng một công việc trên các dữ liệu khác nhau 47
  48. Ví dụ nạp chồng: average  Hàm tính giá trị trung bình của hai số: double average(double n1, double n2) { return ((n1 + n2) / 2.0); }  Hàm tính giá trị trung bình của 3 số: double average(double n1, double n2, double n3) { return ((n1 + n2 + n3) / 3.0); }  Hai hàm có cùng tên 48
  49. Nạp chồng average()  Việc hàm nào được gọi phụ thuộc vào bản thân lời gọi hàm ◦ avg = average(5.2, 6.7); //Gọi average() hai tham số ◦ avg = average(6.5, 8.5, 4.2); //Gọi average() ba tham số  Bộ biên dịch xử lý dựa trên tín hiệu của lời gọi hàm ◦ Khớp lời gọi với hàm phù hợp ◦ Xem xét mỗi hàm riêng biệt  Lưu ý: chỉ nạp chồng các hàm thực hiện cùng một công việc 49
  50. Ví dụ nạp chồng  Cho các hàm sau đây: 1. void f(int n, double m); 2. void f(double n, int m); 3. void f(int n, int m);  Các lời gọi: f(98, 99); Calls #3 f(5.3, 4); Calls #2 f(4.3, 5.2); Calls ???  Tránh việc nạp chồng nhập nhằng 50
  51. Chuyển kiểu tự động và nạp chồng  Tham số dạng số thường ở kiểu "double"  Cho phép bất kỳ kiểu số nào: dữ liệu phụ thuộc tự động được chuyển đổi int double float double char double  Tránh việc nạp chồng cho các kiểu số khác nhau 51
  52. Chuyển kiểu tự động và nạp chồng  double mpg(double miles, double gallons) { return (miles/gallons); } Các lời gọi ví dụ:  mpgComputed = mpg(5, 20); ◦ Chuyển 5 & 20 thành doubles, rồi truyền  mpgComputed = mpg(5.8, 20.2); ◦ Không cần chuyển kiểu  mpgComputed = mpg(5, 2.4); ◦ Chuyển 5 thành 5.0, sau đó truyền các giá trị cho hàm 52
  53. Các tham số mặc định  Cho phép lờ đi một vài tham số  Được chỉ ra trong khai báo hàm void showVolume( int length, int width = 1, int height = 1); ◦ Hai tham số cuối là mặc định  Các lời gọi có thể có: ◦ showVolume(2, 4, 6); //tất cả các tham số được cung cấp ◦ showVolume(3, 5); //height mặc định là 1 ◦ showVolume(7); //width & height mặc định là 1 53
  54. Các tham số mặc định 54
  55. Các tham số mặc định 55
  56. Tóm tắt  Hai kiểu hàm: hàm trả về giá trị và hàm void  Dữ liệu cục bộ: được khai báo trong định nghĩa hàm  Dữ liệu toàn cục: Được khai báo bên ngoài các định nghĩa hàm, phù hợp cho hằng nhưng không phù hợp cho biến  Tham số/Đối số: ◦ Hình thức: Trong khai báo và định nghĩa hàm ◦ Thực sự: Trong lời gọi hàm 56
  57. Tóm tắt  Tham số hình thức là để giữ chỗ, được điền bằng đối số thực sự trong lời gọi hàm  Tham số truyền giá trị là bản sao cục bộ trong thân hàm nhận  Truyền tham chiếu truyền địa chỉ bộ nhớ của đối số thực sự  Được phép viết nhiều định nghĩa cho cùng một tên hàm: được gọi là nạp chồng  Các tham số mặc định cho phép lời gọi hàm lờ đi một số đối số trong danh sách 57