Hàm (function)

1

EE3490: Kỹ thuật lập trình – HK1 2011/2012 Đào Trung Kiên – ĐH Bách khoa Hà Nội

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

}

 Toán tử return dùng để thoát khỏi hàm và trả kết quả

2

EE3490: Kỹ thuật lập trình – HK1 2011/2012 Đào Trung Kiên – ĐH Bách khoa Hà Nội

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

3

EE3490: Kỹ thuật lập trình – HK1 2011/2012 Đào Trung Kiên – ĐH Bách khoa Hà Nội

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() {

/* trả về: 10+20 */

int x = 1, y = 2; int z = sum(); return 0;

}

4

EE3490: Kỹ thuật lập trình – HK1 2011/2012 Đào Trung Kiên – ĐH Bách khoa Hà Nội

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;

return x+y;

int sum(int x, int y) {

} int a = 1000, b = 2000; int main() {

int x = 100, y = 200; x+y;

} x+y; sum(a,b); return 0;

int x = 10, y = 20; {

5

EE3490: Kỹ thuật lập trình – HK1 2011/2012 Đào Trung Kiên – ĐH Bách khoa Hà Nội

}

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);

}

6

EE3490: Kỹ thuật lập trình – HK1 2011/2012 Đào Trung Kiên – ĐH Bách khoa Hà Nội

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;

}

tic_time = clock();

void tic() {

return clock() - tic_time;

} int toc() {

 Hàm static: tự tìm hiểu thêm

7

EE3490: Kỹ thuật lập trình – HK1 2011/2012 Đào Trung Kiên – ĐH Bách khoa Hà Nội

}

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

if (number == a[i])

return i;

}

 Hàm void: không trả về giá trị gì

 void copy(int *a, int *b, int n) {

if (a==NULL || b==NULL || a==b || n==0)

return -1;

return;

for (; n>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

8

EE3490: Kỹ thuật lập trình – HK1 2011/2012 Đào Trung Kiên – ĐH Bách khoa Hà Nội

}

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)

copy

int a = 20; assign10(a); printf("a = %d", a); return 0;

}

 Dùng con trỏ nếu muốn thay đổi giá trị của biến ở nơi gọi

 void assign10(int *x)

a

x (int*)

int a = 20; assign10(&a);

{ *x = 10; } copy

 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

9

EE3490: Kỹ thuật lập trình – HK1 2011/2012 Đào Trung Kiên – ĐH Bách khoa Hà Nội

&a a

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) {

z

int* sum() { }

int z = x+y; return &z;

}

copy địa chỉ

int* p = sum(2, 3);

/* sai */

 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;

p

z

int* sum() { }

}

*z

copy

int* p = sum(2, 3); /* ... */ free(p);

10

EE3490: Kỹ thuật lập trình – HK1 2011/2012 Đào Trung Kiên – ĐH Bách khoa Hà Nội

p

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; }

11

EE3490: Kỹ thuật lập trình – HK1 2011/2012 Đào Trung Kiên – ĐH Bách khoa Hà Nội

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ế

12

EE3490: Kỹ thuật lập trình – HK1 2011/2012 Đào Trung Kiên – ĐH Bách khoa Hà Nội

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 mul(double x, double y) { return x*y; } int main() {

double (*SomeOpt)(double, double) = ∑ SomeOpt(2., 5.); /* sum(2., 5.); */ SomeOpt = mul; (*SomeOpt)(2., 5.); /* mul(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

13

EE3490: Kỹ thuật lập trình – HK1 2011/2012 Đào Trung Kiên – ĐH Bách khoa Hà Nội

}

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

14

EE3490: Kỹ thuật lập trình – HK1 2011/2012 Đào Trung Kiên – ĐH Bách khoa Hà Nội

Macro (tiếp)

 Macro có thể có tham số  đôi khi được dùng như hàm

 #define MIN(x,y) x

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 = MUL(2+1,4); z = 8/MUL(1+1,2);

/* z = 2*4; */ /* z = 2+1*4; */ /* z = 8/1+1*2; */

 #define MUL(x,y) ((x)*(y))

z = MUL(2+1,4); z = 8/MUL(1+1,2);

/* z = ((2+1)*(4)); */ /* z = 8/((1+1)*(2)); */

 #define SQR(x) ((x)*(x))

z = SQR(i); z = SQR(i++);

/* z = ((i)*(i)); */ /* z = ((i++)*(i++)); */

15

EE3490: Kỹ thuật lập trình – HK1 2011/2012 Đào Trung Kiên – ĐH Bách khoa Hà Nội

Thư viện hàm

16

EE3490: Kỹ thuật lập trình – HK1 2011/2012 Đào Trung Kiên – ĐH Bách khoa Hà Nội

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

 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

đã được dịch ra các dạng tương ứng

17

EE3490: Kỹ thuật lập trình – HK1 2011/2012 Đào Trung Kiên – ĐH Bách khoa Hà Nội

vào file đang dịch ở vị trí xuất hiện

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

 #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"  #include

18

EE3490: Kỹ thuật lập trình – HK1 2011/2012 Đào Trung Kiên – ĐH Bách khoa Hà Nội

/* .h cùng thư mục */ /* .h trong thư mục thư viện */

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 l*l; }

{ return r1*r2*PI; } double dt_vuong(double l)

{ return l1*l2; }

19

EE3490: Kỹ thuật lập trình – HK1 2011/2012 Đào Trung Kiên – ĐH Bách khoa Hà Nội

double dt_chu_nhat(double l1, double l2)

Một số thư viện chuẩn

Chức năng Tên Xuất, nhập với màn hình, file, bàn phím,… stdio.h Kiểm tra các lớp ký tự (chữ số, chữ cái,…) ctype.h Xử lý chuỗi và bộ nhớ string.h math.h Một số hàm toán học stdlib.h time.h

Chuyển đổi dữ liệu số-chuỗi, cấp phát bộ nhớ,… Các hàm về thời gian

20

EE3490: Kỹ thuật lập trình – HK1 2011/2012 Đào Trung Kiên – ĐH Bách khoa Hà Nội

Bài tập

1.

2.

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ử Viết hàm prime(…) trả về mảng các số nguyên tố bé hơn n

4.

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 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

21

EE3490: Kỹ thuật lập trình – HK1 2011/2012 Đào Trung Kiên – ĐH Bách khoa Hà Nội