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ả
              
                                            
                                
            
 
            
                 22 trang
22 trang | 
Chia sẻ: phuongt97 | Lượt xem: 643 | Lượt tải: 0 
              
            Bạn đang xem trước 20 trang nội dung 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, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
EE3490: Kỹ thuật lập trình – HK1 2017/2018
TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội
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
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
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
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
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
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
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
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
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<n; i++)
if (number == a[i])
return i;
return -1;
}
 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;
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
9
EE3490: Kỹ thuật lập trình – HK1 2017/2018
TS. Đà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() {
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)
{ *x = 10; }
int a = 20;
assign10(&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
x (int)
a
copy
a
x (int*)
&a
copy
EE3490: Kỹ thuật lập trình – HK1 2017/2018
TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội
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;
return &z;
}
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;
}
int* p = sum(2, 3);
/* ... */
free(p);
11
p
zint* sum() { }
copy
*z
p
zint* sum() { }
copy
địa chỉ
EE3490: Kỹ thuật lập trình – HK1 2017/2018
TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội
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
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
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
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
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
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
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
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
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)
{ return l1*l2; }20
EE3490: Kỹ thuật lập trình – HK1 2017/2018
TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội
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
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
            Các file đính kèm theo tài liệu này:
 bai_giang_ky_thuat_lap_trinh_bai_5_ham_va_thu_vien_dao_trung.pdf bai_giang_ky_thuat_lap_trinh_bai_5_ham_va_thu_vien_dao_trung.pdf