Nội dung
Khái niệm hàm và lập trình cấu trúc
Khai báo và Định nghĩa một hàm trong C++
Lời gọi hàm
Tham số của hàm
Định nghĩa chồng các hàm
Hàm toán tử
Định nghĩa chồng các toán tử
Đệ quy
Tổ chức chương trình dạng đơn thể
HIENLTH, C++ - 2010
HIENLTH, C++ - 2010
2
Hàm thư viện
Khái niệm hàm và lập trình cấu trúc
Hàm đã được chuẩn hóa, sẵn sàng cho dùng lại.
Toán học (cmath)
Xử lý xâu ký tự (cstring)
Hàm nhập/xuất (cin/cout)
Tư tưởng chính
Chia bài toán lớn thành các bài toán nhỏ, hoặc phân
rã quá trình giải bài toán thành một số hữu hạn các
bước.
Với mỗi bài toán con hoặc một bước giải bài toán, xây
dựng một (hoặc nhiều) hàm (thủ tục) giải quyết.
Mỗi hàm (thủ tục) là một đơn vị hoàn chỉnh, độc
lập về đoạn mã và dữ liệu nhằm thi hành một tác
vụ nào đó.
Mỗi hàm (thủ tục) nên được thiết kế chỉ để thi
hành một và chỉ một tác vụ duy nhất.
HIENLTH, C++ - 2010
3
HIENLTH, C++ - 2010
4
Một số nguyên tắc
Các hàm trong C++ đều ngang cấp với nhau:
Hàm không được khai báo lồng nhau.
Thứ tự khai báo không quan trọng.
Hàm có thể nhận và xử lý nhiều tham số hoặc
không có tham số nào
Hàm có thể trả về một giá trị hoặc không.
Biến khai báo trong hàm F chỉ có giá trị trong F,
không sử dụng được biến này trong các hàm
khác được.
Ví dụ: hàm tính xn
double
Power(double x, int n)
{
double result;
for(result = 1; n; n--)
result *= x;
return result;
}
giá trị được trả về qua lệnh return
HIENLTH, C++ - 2010
Ví dụ: gọi thực hiện hàm Power
HIENLTH, C++ - 2010
Một số lỗi thường gặp
Chỉ thị cho chương trình biết prototype của hàm Power
#include <iostream>
using namespace std;
double Power(double, int);
int
{
Compiler không hiểu được hàm Power
#include <iostream>
using namespace std;
int
{
main()
double m = Power(2, 3);
cout<<“3.5 ^ 4 = ”<
return 0;
nhận vào 2 tham số khi được gọi
kiểu của giá trị trả về
hàm Power thiếu tham số
main()
int m = Power(2, 3);
cout<<“3.5 ^ 4 = ”<
return 1.0;
}
}
3.5 và 4: 2 tham số thực sự
HIENLTH, C++ - 2010
giá trị trả về không khớp kiểu
HIENLTH, C++ - 2010
Khai báo hàm dạng nguyên mẫu
(prototype)
Ví dụ:
Mục đích:
void xuatPhanSo(int, int);
Chỉ ra prototype của hàm bao gồm: tên hàm, kiểu các
tham số và kiểu trả về.
Báo cho trình biên dịch biết rằng có một hàm như vậy
hàm có tên là xuatPhanSo
có hai tham số; cả hai đều có kiểu là số nguyên (int)
không có giá trị trả về
Cú pháp:
void xuatPhanSo(PhanSo);
void nhapPhanSo(int&, int&);
<kiểu trả về> <tên hàm> (
các tham số> );
Trong đó:
Hàm có tên là nhapPhanSo
Có hai tham số; cả hai đều có kiểu tham chiếu đến số
nguyên (int&)
Không có giá trị trả về
<kiểu trả về>: là một kiểu do C++ hỗ trợ hoặc do
người dùng tạo ra
<tên hàm>: tên của hàm
<danh sách kiểu các tham số>: chỉ ra kiểu của
các tham số của hàm
HIENLTH, C++ - 2010
void nhapPhanSo(PhanSo&);
9
Ví dụ:
HIENLTH, C++ - 2010
10
Khai báo một hàm (tt)
Có thể đưa vào tên của tham số (không chỉ kiểu
của tham số)
Ví dụ:
void xuatPhanSo(int tuso, int mauso);
void nhapPhanSo(int& tuso, int& mauso);
double tinhLuong(double thamnien);
void thongbaoLoi();
double tinhLuong(double);
Hàm có tên là: tinhLuong
Có một tham số, có kiểu là số thực (double)
Có giá trị trả về là số thực
void thongbaoLoi();
Hàm có tên là: thongbaoLoi
Không có tham số
Không có giá trị trả về
HIENLTH, C++ - 2010
11
HIENLTH, C++ - 2010
12
Định nghĩa một hàm
Định nghĩa một hàm
Mục đích:
Lưu ý:
Chỉ rõ cụ thể việc cài đặt của hàm
Các công việc mà hàm sẽ làm
Dữ liệu và đoạn mã của hàm sử dụng
Danh sách tham số phải có kiểu trùng với danh sách
kiểu tham số đã được khai báo trước đó.
Trong danh sách các tham số phải có tên tham số
Kết thúc hàm và trả về giá trị cho lời gọi hàm:
return <giá trị trả về>;
Cú pháp:
kiểu trả về tên hàm(danh sách tham số hình thức)
{
//khai báo các biến của hàm
//các lệnh thực thi
return giá trị trả về; //hàm void không có giá trị trả về
}
HIENLTH, C++ - 2010
13
HIENLTH, C++ - 2010
Ví dụ
Ví dụ
Khai báo hàm:
void xuatPhanSo(PhanSo);
Định nghĩa hàm:
void xuatPhanSo(PhanSo ps)
{
cout << ps.tu << “/ “ << ps.mau;
}
// khai báo hàm
void xuatPhanSo(int, int);
…
// định nghĩa hàm
void xuatPhanSo(int tuso, int mauso)
{
cout << tuso << “/” << mauso;
}
HIENLTH, C++ - 2010
15
HIENLTH, C++ - 2010
14
Thân hàm
16
Ví dụ
Ví dụ
// khai báo hàm
void nhapPhanSo(int&, int&);
…
// định
nh nghĩa
ngh a hàm
void nhapPhanSo(int& tuso, int& mauso)
{
cout << “Nhập vào tử số và mẫu số: “;
cin >> tuso >> mauso;
}
Thân hàm
Khai báo hàm:
void nhapPhanSo(PhanSo&);
Định nghĩa hàm
void nhapPhanSo(PhanSo& ps)
{
cout << “Nhập vào tử và mẫu: “;
cin >> ps.tu >> ps.mau;
}
HIENLTH, C++ - 2010
17
Ví dụ
HIENLTH, C++ - 2010
18
Lời gọi hàm
// khai báo hàm
double tinhLuong(double);
…
// định nghĩa hàm
double tinhLuong(double thamnien)
{
Thân hàm
const double LCB = 540000;
double heso, luong;
if (thamnien < 12)
heso = 1.92;
else if (thamnien < 36)
heso = 2.34;
else
heso = 3.5;
Kết thúc hàm và trả về giá
luong = heso * LCB;
trị cho lời gọi hàm
return luong;
}
HIENLTH, C++ - 2010
Mục đích:
“Gọi” một hàm đã được khai báo và định nghĩa
Trả về giá trị sau khi hàm được gọi thi hành xong
Cú pháp:
<tên hàm> (<các tham số thật sự>)
Trong đó:
<tên hàm>: tên của hàm muốn gọi. Hàm đó phải
được khai báo trước đó.
<các tham số thật sự>: các tham số thật sự cho
lời gọi hàm này.
19
HIENLTH, C++ - 2010
20
Ý nghĩa của định nghĩa hàm và lời gọi
hàm
Ví dụ
int main()
{
int a, b;
nhapPhanSo(a, b);
xuatPhanSo(a, b);
return 0;
}
void xuatPhanSo(int tuso, int mauso)
{
cout << tuso << “/” << mauso;
}
Gọi hàm nhapPhanSo với tham
số thực là biến a và biến b
xuatPhanSo(tuso, mauso)
Gọi hàm xuatPhanSo với tham
số thực là giá trị của a và giá trị
của b
HIENLTH, C++ - 2010
Xuất ra màn hình một Xuất ra màn hình một
phân số có tử và mẫu phân số có tử là 4 và
mẫu là 5
là số nguyên
21
Ý nghĩa của định nghĩa hàm và lời gọi
hàm
nhapPhanSo(a, b)
Nhập vào giá trị cho tử
số và mẫu số của một
phân số nào đó
Nhập vào giá trị cho tử
số a và mẫu số b
HIENLTH, C++ - 2010
HIENLTH, C++ - 2010
22
Ý nghĩa của định nghĩa hàm và lời gọi
hàm
double tinhLuong(double thamnien)
{
const double LCB = 54000;
double heso, luong;
if (thamnien < 12)
heso = 1.92;
void nhapPhanSo(int& tuso, int& mauso)
{
cout << “Nhập vào ttử ssố và m
mẫu
u ssố:: “;
cin >> tuso >> mauso;
}
nhapPhanSo(tuso, mauso)
xuatPhanSo(4, 5)
else if (thamnien < 36)
heso = 2.34;
else
heso = 3.5;
luong = heso * LCB;
return luong;
}
23
tinhLuong(thamnien)
tinhLuong(30)
Tính lương cho một nhân viên
nào đó có thâm niên nào đó
Tính lương cho nhân viên có
thâm niên công tác 30 tháng
Trả về: lương của nhân viên
(chưa xác định)
Trả về lương của nhân viên
(2.34 * 540000)
HIENLTH, C++ - 2010
24
Tiến trình thi hành một lời gọi hàm
Luồng điều khiển được tạm thời chuyển sang
cho hàm được gọi.
Thực hiện các lệnh nằm trong thân hàm được
gọi.
Nếu gặp câu lệnh return thì kết thúc hàm được
gọi; chuyển quyền điều khiển lại cho nơi gọi
hàm; trả về kết quả cho lời gọi hàm.
Nếu không có giá trị trả về thì đến khi kết thúc tất
cả các lệnh trong thân hàm được gọi sẽ chuyển
quyền điều khiển cho nơi gọi hàm.
HIENLTH, C++ - 2010
25
Tham số của hàm
void nhapPhanSo(int& tuso,
int& mauso)
{
cout << “Nhập vào tử số và
mẫu số: “;
cin >> tuso >> mauso;
}
void xuatPhanSo(int tuso, int
mauso)
{
cout << tuso << “/” <<
mauso;
}
HIENLTH, C++ - 2010
26
Tham số của hàm (tt)
Có 3 dạng:
Tham trị - call by value
Con trỏ - call by address
Tham chiếu - call by reference
Tham trị
Quan hệ giữa tham số thực
th c và tham số hình thức trong
định nghĩa hàm là quan hệ giá trị.
Khi có một lời gọi hàm, các tham số hình thức sẽ được cấp
phát vùng nhớ.
Giá trị của tham số hình thức sẽ được gán bởi giá trị của
các tham số thực.
Tham số hình thức và tham số thực trở nên hoàn toàn độc
lập. Thay đổi trên tham số hình thức không ảnh hưởng đến
tham số thực.
HIENLTH, C++ - 2010
int main()
{
int a, b;
nhapPhanSo(a, b);
xuatPhanSo(a, b);
return 0;
}
27
Con trỏ: (*)
Một trường hợp đặc biệt của tham trị
Giá trị ở đây là giá trị địa chỉ do con trỏ (tham
số thực) đang lưu.
Hoạt động theo tính chất của con trỏ.
(Sẽ được học kỹ hơn trong phần Con trỏ)
HIENLTH, C++ - 2010
28
Truyền giá trị - ví dụ
Tham số của hàm (tt)
Tham chiếu (&)
Quan hệ giữa tham số hình thức và tham số
thực là quan hệ “bí danh”: một vùng nhớ được
đặt theo 2 tên khác nhau; cả 2 tên đều cùng
chỉ đến một vùng nhớ.
Khi có một lời gọi hàm, tham số thực truyền
theo kiểu tham chiếu sẽ được đặt thêm một
tên mới, chính là tên của tham số hình thức.
Mọi thay đổi trên tham số hình thức cũng tức
là thay đổi trên tham số thực.
29
HIENLTH, C++ - 2010
#include <iostream>
using namespace std;
void change(int v);
hàm change
không thay đổi
giá trị của “var”
int main()
{
int var = 5;
change(var);
cout<<"main: var = “<
return 0;
change: v = 500
}
main: var = 5
void change(int v)
{
v *= 100;
cout<<"change: v = “<
}
HIENLTH, C++ - 2010
Ví dụ tham trị: nhập a=4, b=5
Ví dụ tham chiếu: nhập a=4, b=5
void main()
{
int a, b;
nhapPhanSo(a, b);
xuatPhanSo(a, b);
}
void main()
{
int a, b;
nhapPhanSo(a, b);
xuatPhanSo(a, b);
}
a
b
4
void nhapPhanSo(int& tuso, int& mauso)
{
cout << “Nhập vào tử số và mẫu số: “;
cin >> tuso >> mauso;
}
void xuatPhanSo(int tuso, int mauso)
{
cout << tuso << “/” << mauso;
}
tuso
5
4
HIENLTH, C++ - 2010
a, tuso
mauso
4
5
31
b, mau
5
void nhapPhanSo(int& tuso, int&
mauso)
{
cout << “Nhập vào tử số và
mẫu số: “;
cin >> tuso >> mauso;
}
void xuatPhanSo(int tuso, int
mauso)
{
cout << tuso << “/” << mauso;
}
HIENLTH, C++ - 2010
32
Tham số mặc định
Ví dụ 1
// khai báo hàm
void xuatPhanSo(int tuso = 0, int mauso = 1);
…
// Sử dụng hàm
// định nghĩa hàm
void main()
void xuatPhanSo(int tuso, int mauso)
{
{
xuatPhanSo();
cout << tuso << “/” << mauso;
xuatPhanSo(4);
}
xuatPhanSo(2, 5);
…
Công dụng
Cho phép truyền tham số một cách linh họat
Nếu có truyền thì lấy giá trị truyền vào
Nếu không có truyền thì lấy giá trị mặc định
Cú pháp:
Khai báo hàm:
<kiểu trả về> <tên hàm> (kiểu tham_số_1 =
giá_trị[,kiểu tham_số_1 = giá_trị]);
Lưu ý:
Định nghĩa hàm vẫn như bình thường
Tham số mặc định bắt buộc phải đi từ phải qua trái
trái,
không có tham số không mặc định chèn ở giữa
HIENLTH, C++ - 2010
}
33
HIENLTH, C++ - 2010
Ví dụ 2
Ví dụ 3
// khai báo hàm
void xuatPhanSo(int tuso, int mauso = 1);
…
// Sử dụng hàm
// định nghĩa hàm
void main()
void xuatPhanSo(int tuso, int mauso)
{
{
xuatPhanSo();
cout << tuso << “/” << mauso;
xuatPhanSo(4);
}
xuatPhanSo(2, 5);
…
}
// khai báo hàm
void xuatPhanSo(int tuso = 1, int mauso);
…
// Sử dụng hàm
// định nghĩa hàm
void xuatPhanSo(int tuso, int mauso) void main()
{
{
xuatPhanSo();
cout << tuso << “/” << mauso;
xuatPhanSo(4);
}
xuatPhanSo(2, 5);
…
HIENLTH, C++ - 2010
35
34
}
HIENLTH, C++ - 2010
36
Quá tải hàm
Quá tải hàm (tt)
Dấu hiệu của hàm (signature)
Dùng để phân biệt các hàm với nhau. Mỗi hàm
trong chương trình phải có một dấu hiệu duy
nhất.
Bao gồm:
Tên hàm
Kiểu tham số của hàm
Số tham số của hàm
void xuatPhanSo(double tuso, double mauso);
Có kí hiệu là xuatPhanSo_double_double
Quá tải hàm
Hai hàm có cùng tên, có cùng số tham số và tồn tại
ít nhất một tham số khác nhau thì khác nhau
Ví dụ: void xuatPhanSo(int tuso, int mauso);
Khác với
void xuatPhanSo(double tuso, double mauso);
Hai hàm có cùng tên, khác số tham số thì khác nhau
Ví dụ:
Khác với
void xuatPhanSo(int tuso, int mauso);
Có kí hiệu là xuatPhanSo_int_int
HIENLTH, C++ - 2010
void xuatPhanSo(int tuso, int mauso);
void xuatPhanSo(int tuso);
37
Ví dụ 1
HIENLTH, C++ - 2010
38
Ví dụ 2
void main()
{
xuatPhanSo(1, 2);
xuatPhanSo(1.0, 2.0);
double a, b;
cin >> a >> b;
xuatPhanSo(a, b);
}
void xuatPhanSo(int tuso, int mauso)
{
cout << tuso << “/” << mauso;
}
void xuatPhanSo(double tuso, double
mauso)
{
cout << tuso << “/” << mauso;
}
HIENLTH, C++ - 2010
39
void main()
{
int a, b;
nhapPhanSo(a, b);
xuatPhanSo(a, b);
xuatPhanSo(a);
}
void xuatPhanSo(int tuso, int mauso)
{
cout << tuso << “/” << mauso;
}
void xuatPhanSo(int tuso)
{
cout << tuso;
}
HIENLTH, C++ - 2010
40
Quá tải toán tử
Quá tải toán tử (tt)
Một toán tử (phép toán) thực chất là một hàm có
dạng như sau:
Chính nhờ kí hiệu của hàm bao gồm tên hàm và
các tham số mà ta có thể quá tải toán tử. Bởi vì
các toán tử cùng chung kí hiệu đều có cùng tên
hàm là
operator <kí hiệu phép toán>
<kiểu trả về> operator
toán> ( <danh sách các tham số> );
;
Trong đó danh sách các tham số là các toán
hạng
Ví dụ:
3+4: là phép toán + có hai toán hạng là hai số nguyên;
kết quả trả về một số nguyên
int operator + (int a, int b)
Ứng dụng:
Dùng tóan tử xuất, nhập trên các đối tượng
một cách bình thường.
41
HIENLTH, C++ - 2010
Ví dụ 1
42
HIENLTH, C++ - 2010
Ví dụ 2
void main()
{
A a;
B b;
//int c = a+b;
int c = b+a;
cout << c;
cin.get();
}
class A{
public:
int a;
A():a(0){};
};
class B{
public:
int b;
B():b(10){};
};
int operator + (A aObj, B bObj)
{
return aObj.a+bObj.b;
}
HIENLTH, C++ - 2010
class A{
public:
int a;
A():a(0){};
};
ostream& operator << (ostream& out, A aObj)
{
return out << aObj.a;
}
43
HIENLTH, C++ - 2010
void main()
{
A a;
B b;
int c = a+b;
cout << c;
cout << a;
cin.get();
}
44
Bài đọc thêm: tổ chức dữ liệu
Lưu ý:
Dữ liệu trong chương trình được lưu trữ trong các biến.
Khi hàm được gọi thực hiện, các biến cục bộ sẽ được
khởi tạo trên vùng nhớ stack và tự động bị hủy khi hàm
kết thúc.
Các biến toàn cục sẽ được tạo trên vùng nhớ phân
đoạn dữ liệu (data segment) khi chương trình được gọi
thực hiện, tự động bị hủy khi chương trình kết thúc.
Có thể sử dụng các từ khóa để chỉ định vị trí của biến:
auto
- stack (default)
static
- data segment
register
- thanh ghi của CPU
Môn này không cung cấp các kiến thức chuyên
sâu về OOP (lập trình hướng đối tượng),
operator overloading (quá tải toán tử),… SV tự
tìm hiểu + được học ở môn học kế tiếp OOP
Heap
Data
segment
Stack
Dữ liệu còn có thể được đặt trong vùng nhớ heap.
HIENLTH, C++ - 2010
45
Đệ quy
HIENLTH, C++ - 2010
Ví dụ: Hàm tính số Fn dãy Fibonaci
Cấu trúc đệ quy
Tự định nghĩa qua chính nó
Có cấu trúc nguyên thủy
Hàm đệ quy
Gọi lại chính nó (trực tiếp hay gián tiếp)
Có điểm dừng
HIENLTH, C++ - 2010
int Fibo(int n)
{
if (n < 1) return 0;
if (1 == n) return 1;
return Fibo (n-1) + Fibo (n-2);
}
47
HIENLTH, C++ - 2010
48
Ứng dụng đệ quy
Ví dụ: Hàm tính số n!
Bài toán có cấu trúc đệ quy
Thuật toán chia để trị
Tìm kiếm quay lui
…
int Factor(int n)
{
if (n <= 1) return 1;
return Factor (n-1) * n;
}
HIENLTH, C++ - 2010
49
Đệ quy và lặp
HIENLTH, C++ - 2010
50
Tổ chức chương trình dạng đơn thể
Đệ quy
Súc tích, dễ hiểu
Có thể không hiệu quả
Phân thành các hàm, module
Tổ chức chương trình thành Header file (*.h) và
Source Code (*.cpp)
File *.h:
Phí tổn cho lời gọi hàm
Lặp lại các kết quả đã có
chỉ chứa khai báo thư viện
Khai báo các hàm dạng nguyên mẫu(prototype)
Nên chuyển thành chương trình lặp (nếu có thể)
Giai thừa, Fibonaci
File *.cpp:
Chèn file header đã khai báo
Cài đặt các hàm đã khai báo nguyên mẫu ở file *.h
HIENLTH, C++ - 2010
51
HIENLTH, C++ - 2010
52
Ví dụ
Thư viện cmath
Khai báo sử dụng thư viện
//File Vidu.cpp
//File Vidu.h
#include <cmath>
#include <iostream>
using namespace std;
long tinhGiaiThua(int n);
void nhapThongTin();
#include “Vidu.h”
Một số hàm cơ bản
long tinhGiaiThua(int n){
//…….
}
void nhapThongTin(){
//……
}
HIENLTH, C++ - 2010
53
55
Hàm toán
Tên
Hàm toán Tên
Hàm toán
asin
acos
atan
arcsin(x)
arccos(x)
arctan(x)
sin
cos
tan
sin(x)
cos(x)
tan(x)
log
sqrt
ln(x)
x1/2
pow
exp
xa
ex
abs
ceil
floor
fabs
|x|:int
cận trên
cận dưới
|x|:double
labs
|x|:long
HIENLTH, C++ - 2010
Câu hỏi và thảo luận
HIENLTH, C++ - 2010
Tên
͡͡
54