Bài giảng Kỹ thuật lập trình - Bài 5: Hàm và thư viện - Đào Trung Kiên

pdf 22 trang ngocly 3510
Bạn đang xem 20 trang mẫu của tài liệu "Bài giảng Kỹ thuật lập trình - Bài 5: Hàm và thư viện - Đào Trung Kiên", để 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:

  • pdfbai_giang_ky_thuat_lap_trinh_bai_5_ham_va_thu_vien_dao_trung.pdf

Nội dung text: Bài giảng Kỹ thuật lập trình - Bài 5: Hàm và thư viện - Đào Trung Kiên

  1. Bài 5: Hàm và thư viện 1 EE3490: Kỹ thuật lập trình – HK1 2017/2018 TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội
  2. Hàm (functions) 2 EE3490: Kỹ thuật lập trình – HK1 2017/2018 TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội
  3. Khái niệm  Hàm là một khối các câu lệnh thực hiện một nhiệm vụ nhất định, và có thể được gọi khi cần  Mỗi hàm có một tên (các hàm trong C không được trùng tên nhau), một số tham số, và một giá trị trả về  Sử dụng hàm giúp:  Chia nhỏ chương trình thành nhiều bài toán con  Sử dụng lại trong một hoặc nhiều chương trình  Cách khai báo:  ( ) { Khai báo các biến dùng cho hàm Các câu lệnh của hàm }  Câu lệnh return dùng để thoát khỏi hàm và trả kết quả 3 EE3490: Kỹ thuật lập trình – HK1 2017/2018 TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội
  4. Ví dụ  Hàm tính tổng hai số  double sum(double x, double y) { double z = x+y; return z; } int main() { double x = 10, y = sum(2,3); printf("x + y = %g", sum(x,y)); return 0; }  Các tham số và các biến nội bộ chỉ giới hạn trong phạm vi của hàm 4 EE3490: Kỹ thuật lập trình – HK1 2017/2018 TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội
  5. Phạm vi của biến, hằng  Biến toàn cục: được khai báo ở ngoài các hàm, có phạm vi trong toàn chương trình và tồn tại trong suốt quá trình chạy  Biến địa phương: được khai báo ở trong một hàm hoặc một khối lệnh, chỉ có phạm vi trong hàm/khối đó, và bị huỷ sau khi kết thúc chạy hàm/khối đó  Khai báo biến địa phương sẽ “che” mất biến cùng tên khác có phạm vi rộng hơn  Trong C, biến địa phương phải được khai báo ở đầu hàm hoặc khối lệnh  Ví dụ biến địa phương của hàm:  int x = 10, y = 20; /* phải khai báo trước hàm sum() */ int sum() { int z = x+y; return z; } int main() { int x = 1, y = 2; int z = sum(); /* trả về: 10+20 */ return 0; } 5 EE3490: Kỹ thuật lập trình – HK1 2017/2018 TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội
  6. Biến trong khối lệnh  Trong một khối lệnh { } ta có thể khai thêm biến, biến đó chỉ tồn tại từ khi chương trình chạy vào tới khi thoát khỏi khối lệnh đó  Ví dụ:  int x = 1, y = 2; int sum(int x, int y) { return x+y; } int a = 1000, b = 2000; int main() { int x = 10, y = 20; { int x = 100, y = 200; x+y; } x+y; sum(a,b); return 0; } 6 EE3490: Kỹ thuật lập trình – HK1 2017/2018 TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội
  7. Biến trong khối lệnh: vòng lặp  Chỉ có phạm vi trong một lần chạy của vòng lặp, mỗi lần lặp sẽ tạo ra biến mới và khởi tạo lại  Ví dụ:  int x = 20; for (i=0; i<10; i++) { int y = 20; x++; y++; printf("%d %d\n", x, y); } 7 EE3490: Kỹ thuật lập trình – HK1 2017/2018 TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội
  8. Biến static  Là biến chỉ có phạm vi địa phương nhưng vẫn tồn tại ngay cả khi chưa vào hoặc đã thoát khỏi hàm/khối  Khai báo bằng cách thêm từ khoá static  int callCount() { static int count = 0; count++; return count; }  Cũng có biến static toàn cục: thuộc nội bộ của một file nguồn  static int tic_time = 0; void tic() { tic_time = clock(); } int toc() { return clock() - tic_time; }  Hàm static: tự tìm hiểu thêm 8 EE3490: Kỹ thuật lập trình – HK1 2017/2018 TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội
  9. Câu lệnh return  Kết thúc hàm và trả về một giá trị cho nơi gọi nó  int find(int number, int a[], int n) { int i; for (i=0; i 0; n ) *a++ = *b++; }  Câu lệnh return không có tham số  Không cần lệnh return ở cuối hàm 9 EE3490: Kỹ thuật lập trình – HK1 2017/2018 TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội
  10. Tham số kiểu giá trị và kiểu tham chiếu  Tham số của hàm là biến tạm thời, tạo ra khi gọi và huỷ khi hàm kết thúc gán giá trị cho tham số không ảnh hưởng tới biến nơi gọi  void assign10(int x) { x = 10; } int main() { x (int) int a = 20; assign10(a); copy printf("a = %d", a); return 0; a }  Dùng con trỏ nếu muốn thay đổi giá trị của biến ở nơi gọi  void assign10(int *x) x (int*) { *x = 10; } int a = 20; copy assign10(&a); &a a  Tham số con trỏ thường được dùng như một cách khác để trả về thêm giá trị, vì mỗi hàm chỉ có một giá trị trả về theo đúng nghĩa 10 EE3490: Kỹ thuật lập trình – HK1 2017/2018 TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội
  11. Hàm trả về con trỏ  Vấn đề với hàm trả về biến địa phương  int* sum(int x, int y) { int z = x+y; int* sum() { z } return &z; copy } địa chỉ int* p = sum(2, 3); /* sai */ p  Cấp phát bộ nhớ trong hàm  int* sum(int x, int y) { int* z = (int*)malloc(sizeof(int)); *z = x+y; return z; int* sum() { z } } *z copy int* p = sum(2, 3); /* */ free(p); p 11 EE3490: Kỹ thuật lập trình – HK1 2017/2018 TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội
  12. Nguyên mẫu (prototype) của hàm  Là việc khai báo hàm trước, nội dung của nó được triển khai sau thường khai báo ở đầu file hoặc trong file .h  Ví dụ:  double tong(double x, double y); double tich(double x, double y); int main() { double x = 5., y = 10.; tong(x, y); tich(x, y); return 0; } double tong(double x, double y) { return x+y; } double tich(double x, double y) { return x*y; } 12 EE3490: Kỹ thuật lập trình – HK1 2017/2018 TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội
  13. Hàm đệ quy (recursive function)  Là hàm có câu lệnh gọi chính nó  Ví dụ 1: giai thừa một số n  unsigned int giai_thua(unsigned int n) { if (n <= 1) return 1; return n * giai_thua(n-1); }  Ví dụ 2: x mũ n  double mu(double x, unsigned int n) { double y; if (n == 0) return 1; y = mu(x, n/2); if (n%2 == 0) return y*y; return y*y*x; }  Không hiệu quả nếu sinh quá nhiều lệnh gọi hạn chế 13 EE3490: Kỹ thuật lập trình – HK1 2017/2018 TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội
  14. Con trỏ hàm  Là con trỏ trỏ tới một hàm là một kiểu dữ liệu trong C, thường được dùng để gọi một hàm chưa biết trước  double (*SomeOpt)(double, double);  typedef double (*OptFunc)(double, double); OptFunc SomeOpt;  Ví dụ:  double sum(double x, double y) { return x+y; } double prod(double x, double y) { return x*y; } int main() { double (*SomeOpt)(double, double) = ∑ SomeOpt(2., 5.); /* sum(2., 5.); */ SomeOpt = prod; (*SomeOpt)(2., 5.); /* prod(2., 5.); */ return 0; }  Phép gán có thể dùng & hoặc không, gọi cũng có thể dùng * hoặc không 14 EE3490: Kỹ thuật lập trình – HK1 2017/2018 TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội
  15. Macro  Macro là đoạn mã được đại diện bằng một tên, mà mỗi khi tên đó xuất hiện trong chương trình thì sẽ được thay thế bằng đoạn mã tương ứng  #define ERROR { printf("Error, exit now!"); exit(-1); } int main(int argc, char* argv[]) { if (argc != 3) ERROR /* */ return 0; }  Macro có thể được thay thế khi định nghĩa macro khác cùng tên  Huỷ bỏ macro đã định nghĩa: #undef ERROR  Kiểm tra xem macro đã định nghĩa chưa  #ifdef ERROR /* */ #else /* */ #endif 15 EE3490: Kỹ thuật lập trình – HK1 2017/2018 TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội
  16. Macro (tiếp)  Macro có thể có tham số đôi khi được dùng như hàm  #define MIN(x,y) x<y ? x:y z = MIN(2,4); /* z = 2<4 ? 2:4; */  #define PI 3.1415 #define AREA(R) R*R*PI z = AREA(5); /* z = 5*5*3.1415; */  Chú ý các hiệu ứng phụ  #define MUL(x,y) x*y z = MUL(2,4); /* z = 2*4; */ z = MUL(2+1,4); /* z = 2+1*4; */ z = 8/MUL(1+1,2); /* z = 8/1+1*2; */  #define MUL(x,y) ((x)*(y)) z = MUL(2+1,4); /* z = ((2+1)*(4)); */ z = 8/MUL(1+1,2); /* z = 8/((1+1)*(2)); */  #define SQR(x) ((x)*(x)) z = SQR(i); /* z = ((i)*(i)); */ z = SQR(i++); /* z = ((i++)*(i++)); */ 16 EE3490: Kỹ thuật lập trình – HK1 2017/2018 TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội
  17. Thư viện hàm (libraries) 17 EE3490: Kỹ thuật lập trình – HK1 2017/2018 TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội
  18. Thư viện hàm  Một chương trình có thể được chia nhỏ làm nhiều file, mỗi file chứa một nhóm những hàm liên quan tới một phần của chương trình  Một số hàm có thể được dùng trong nhiều chương trình khác nhau thư viện hàm  Một thư viện hàm gồm 2 phần:  File header có đuôi .h chứa prototype các hàm có thể dùng được của thư viện  File mã nguồn có đuôi .c chứa nội dung các hàm, hoặc file .obj, .lib nếu đã được dịch ra các dạng tương ứng  Dùng thư viện hàm trong một file mã nguồn:  #include /* trong đường dẫn mặc định */  #include "ten_file.h" /* cùng thư mục với file dịch */  Dẫn hướng #include có tác dụng như chèn nội dung file được khai báo vào file đang dịch ở vị trí xuất hiện 18 EE3490: Kỹ thuật lập trình – HK1 2017/2018 TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội
  19. Lưu ý với tạo và sử dụng file .h  Trong file abcd.h  Để tránh lỗi khi bị #include nhiều lần, thêm vào đầu và cuối  #pragma once  #ifndef __ABCD_H__ #define __ABCD_H__ /* Nội dung file abcd.h */ #endif  Các biến toàn cục phải được khai báo trong file .c, nếu muốn được export thì trong file .h khai báo thêm bằng extern:  extern int bien_toan_cuc;  Sử dụng file abcd.h  #include "abcd.h" /* .h cùng thư mục */  #include /* .h trong thư mục thư viện */ 19 EE3490: Kỹ thuật lập trình – HK1 2017/2018 TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội
  20. Ví dụ: Thư viện tính diện tích các hình  dientich.h  #ifndef __DIENTICH_H__ #define __DIENTICH_H__ extern const double PI; double dt_tron(double r); double dt_elip(double r1, double r2); double dt_vuong(double l); double dt_chu_nhat(double l1, double l2); #endif  dientich.c  const double PI = 3.1415; double dt_tron(double r) { return r*r*PI; } double dt_elip(double r1, double r2) { return r1*r2*PI; } double dt_vuong(double l) { return l*l; } double dt_chu_nhat(double l1, double l2) 20 { return l1*l2; } EE3490: Kỹ thuật lập trình – HK1 2017/2018 TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội
  21. Một số thư viện chuẩn Tên Chức năng stdio.h Xuất, nhập với màn hình, file, bàn phím, ctype.h Kiểm tra các lớp ký tự (chữ số, chữ cái, ) string.h Xử lý chuỗi và bộ nhớ memory.h Cấp phát và quản lý bộ nhớ động math.h Một số hàm toán học stdlib.h Chuyển đổi dữ liệu số-chuỗi, cấp phát bộ nhớ, time.h Các hàm về thời gian 21 EE3490: Kỹ thuật lập trình – HK1 2017/2018 TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội
  22. Bài tập 1. Viết hàm cấp phát bộ nhớ và nhập giá trị cho một mảng, trả về con trỏ mảng và số phần tử 2. Viết hàm prime( ) trả về mảng các số nguyên tố bé hơn n 3. Định nghĩa mảng các struct MenuItem { Tiêu đề, Hàm xử lý }, in ra màn hình menu, nhận lựa chọn của người dùng và thực hiện chức năng tương ứng 4. Viết hàm tính số Fibonacci thứ n được định nghĩa: Fib0 = 0, Fib1 = 1 Fibn = Fibn-1 + Fibn-2 (n ≥ 2) 5. Định nghĩa kiểu chuỗi String và viết thư viện một số hàm xử lý chuỗi: khởi tạo, copy, nối, tìm kiếm, 6. Định nghĩa kiểu struct Shape rồi viết thư viện có hàm tính diện tích, chu vi của hình tuỳ theo dạng của nó. Dùng 2 cách: switch và con trỏ hàm 22 EE3490: Kỹ thuật lập trình – HK1 2017/2018 TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội