Lập trình C++ - Chương 4: Hàm

Chương này môtảnhững hàmdo người dùng định nghĩa nhưlà một trong

những khối chương trình C++. Hàmcung cấp một phương thức để đóng gói

quá trình tính toán một cách dễdàng để được sửdụng khi cần. Định nghĩa

hàm gồmhai phần: giao diện và thân.

Phần giao diện hàm(cũng được gọi là khai báo hàm) đặc tảhàm có thể

được sửdụng nhưthếnào. Nó gồmba phần:

• Tên hàm. Đây chỉlà một định danh duy nhất.

• Các tham sốcủa hàm. Đây là một tập của không hay nhiều định danh

đã định kiểu được sửdụng đểtruyềncác giá trịtới và từhàm.

• Kiểu trảvềcủa hàm. Kiểu trảvềcủa hàm đặc tảcho kiểu của giá trịmà

hàmtrảvề. Hàmkhông trảvềbất kỳkiểu nào thì nên trảvềkiểu void.

Phần thân hàmchứa đựng các bước tính toán (các lệnh).

pdf12 trang | Chuyên mục: C/C++ | Chia sẻ: dkS00TYs | Lượt xem: 1847 | Lượt tải: 1download
Tóm tắt nội dung Lập trình C++ - Chương 4: Hàm, để xem tài liệu hoàn chỉnh bạn click vào nút "TẢI VỀ" ở trên
void Foo (void) 
{ 
 auto int xyz; // như là: int xyz; 
 //... 
} 
Điều này ít khi được sử dụng bởi vì tất cả các biến cục bộ mặc định là tự 
động. 
4.6. Biến thanh ghi 
Như được đề cập trước đó, nói chung các biến biểu thị các vị trí bộ nhớ nơi 
mà giá trị của biến được lưu trữ tới. Khi mã chương trình tham khảo tới một 
biến (ví dụ, trong một biểu thức), trình biên dịch phát ra các mã máy truy xuất 
tới vị trí bộ nhớ được biểu thị bởi các biến. Đối với các biến dùng thường 
xuyên (ví dụ như các biến vòng lặp), hiệu xuất chương trình có thể thu được 
bằng cách giữ biến trong một thanh ghi, bằng cách này có thể tránh được truy 
xuất bộ nhớ tới biến đó. 
 Bộ lưu trữ thanh ghi có thể được sử dụng để chỉ định cho trình biên dịch 
biến có thể được lưu trữ trong một thanh ghi nếu có thể. Ví dụ: 
for (register int i = 0; i < n; ++i) 
 sum += i; 
Ở đây mỗi vòng lặp i được sử dụng ba lần: một lần khi nó được so sánh với n, 
một lần khi nó được cộng vào sum, và một lần khi nó được tăng. Vì thế việc 
giữ biến i trong thanh ghi trong suốt vòng lặp for là có ý nghĩa trong việc cải 
thiện hiệu suất chương trình. 
 Chú ý rằng thanh ghi chỉ là một gợi ý cho trình biên dịch, và trong một vài 
trường hợp trình biên dịch có thể chọn không sử dụng thanh ghi khi nó được 
Chương 4: Hàm 50 
yêu cầu làm điều đó. Một lý do để lý giải là bất kỳ máy tính nào cũng có một 
số hữu hạn các thanh ghi và nó có thể ở trong trường hợp tất cả các thanh ghi 
đang được sử dụng. 
 Thậm chí khi lập trình viên không khai báo thanh ghi, nhiều trình biên 
dịch tối ưu cố gắng thực hiện một dự đoán thông minh và sử dụng các thanh 
ghi mà chúng muốn để cải thiện hiệu suất của chương trình. 
 Ý tưởng sử dụng khai báo thanh ghi thường được đề xuất sau cùng; nghĩa 
là sau khi viết mã chương trình hoàn tất lập trình viên có thể xem lại mã và 
chèn các khai báo thanh ghi vào những nơi cần thiết. 
4.7. Hàm nội tuyến 
Giả sử một chương trình thường xuyên yêu cầu tìm giá trị tuyệt đối của một 
số các số nguyên. Cho một giá trị được biểu thị bởi n, điều này có thể được 
giải thích như sau: 
(n > 0 ? n : -n) 
Tuy nhiên, thay vì tái tạo biểu thức này tại nhiều vị trí khác nhau trong 
chương trình, tốt hơn hết là nên định nghĩa nó trong một hàm như sau: 
int Abs (int n) 
{ 
 return n > 0 ? n : -n; 
} 
 Phiên bản hàm có một số các thuận lợi. Thứ nhất, nó làm cho chương 
trình dễ đọc. Thứ hai, nó có thể được sử dụng lại. Và thứ ba, nó tránh được 
hiệu ứng phụ không mong muốn khi đối số chính nó là một biểu thức có các 
hiệu ứng phụ. 
 Tuy nhiên, bất lợi của phiên bản hàm là việc sử dụng thường xuyên có 
thể dẫn tới sự bất lợi về hiệu suất đáng kể vì các tổn phí dành cho việc gọi 
hàm. Ví dụ, nếu hàm Abs được sử dụng trong một vòng lặp được lặp đi lặp lại 
một ngàn lần thì sau đó nó sẽ có một tác động trên hiệu suất. Tổn phí có thể 
được tránh bằng cách định nghĩa hàm Abs như là hàm nội tuyến (inline):
inline int Abs (int n) 
{ 
 return n > 0 ? n : -n; 
} 
 Hiệu quả của việc sử dụng hàm nội tuyến là khi hàm Abs được gọi, trình 
biên dịch thay vì phát ra mã để gọi hàm Abs thì mở rộng và thay thế thân của 
hàm Abs vào nơi gọi. Trong khi về bản chất thì cùng tính toán được thực hiện 
nhưng không có liên quan đến lời gọi hàm vì thế mà không có cấp phát stack. 
Chương 4: Hàm 51 
 Bởi vì các lời gọi tới hàm nội tuyến được mở rộng nên không có vết của 
chính hàm được đưa vào trong mã đã biên dịch. Vì thế, nếu một hàm được 
định nghĩa nội tuyến ở trong một tập tin thì nó không sẵn dùng cho các tập tin 
khác. Do đó, các hàm nội tuyến thường được đặt vào trong các tập tin header 
để mà chúng có thể được chia sẻ. 
 Giống như từ khóa register, inline là một gợi ý cho trình biên dịch thực 
hiện. Nói chung, việc sử dụng inline nên có hạn chế chỉ cho các hàm đơn giản 
được sử dụng thường xuyên mà thôi. Việc sử dụng inline cho các hàm dài và 
phức tạp quá thì chắc chắn bị bỏ qua bởi trình biên dịch. 
4.8. Đệ qui 
Một hàm gọi chính nó được gọi là đệ qui. Đệ qui là một kỹ thuật lập trình 
tổng quát có thể ứng dụng cho các bài toán mà có thể định nghĩa theo thuật 
ngữ của chính chúng. Chẳng hạn bài toán giai thừa được định nghĩa như sau: 
• Giai thừa của 0 là 1. 
• Giai thừa của một số n là n lần giai thừa của n-1. 
Hàng thứ hai rõ ràng cho biết giai thừa được định nghĩa theo thuật ngữ của 
chính nó và vì thế có thể được biểu diễn như một hàm đệ qui: 
int Factorial (unsigned int n) 
{ 
 return n == 0 ? 1 : n * Factorial(n-1); 
} 
 Cho n bằng 3, Bảng 4.1 cung cấp vết của các lời gọi Factorial. Các khung 
stack cho các lời gọi này xuất hiện tuần tự từng cái một trên runtime stack. 
Bảng 4.1 Vết thực thi của Factorial(3). 
Call n n == 0 n * Factorial(n-1) Returns 
Thứ nhất 3 0 3 * Factorial(2) 6 
Thứ hai 2 0 2 * Factorial(1) 2 
Thứ ba 1 0 1 * Factorial(0) 1 
Thứ tư 0 1 1 
 Một hàm đệ qui phải có ít nhất một điều kiện dừng có thể được thỏa. 
Ngược lại, hàm sẽ gọi chính nó vô hạn định cho tới khi tràn stack. Ví dụ hàm 
Factorial có điều kiện dừng là n == 0. (Chú ý đối với trường hợp n là số âm 
thì điều kiện sẽ không bao giờ thỏa và Factorial sẽ thất bại). 
Chương 4: Hàm 52 
4.9. Đối số mặc định 
Đối số mặc định là một thuận lợi lập trình để bỏ bớt đi gánh nặng phải chỉ 
định các giá trị đối số cho tất cả các tham số hàm. Ví dụ, xem xét hàm cho 
việc báo cáo lỗi: 
void Error (char *message, int severity = 0); 
Ở đây thì severity có một đối số mặc định là 0; vì thế cả hai lời gọi sau đều hợp 
lệ: 
Error("Division by zero", 3); // severity đặt tới 3 
Error("Round off error"); // severity đặt tới 0 
Như là lời gọi hàm đầu tiên minh họa, một đối số mặc định có thể được ghi 
chồng bằng cách chỉ định rõ ràng một đối số. 
 Các đối số mặc định là thích hợp cho các trường hợp mà trong đó các 
tham số nào đó của hàm (hoặc tất cả) thường xuyên lấy cùng giá trị. Ví dụ 
trong hàm Error, severity 0 lỗi thì phổ biến hơn là những trường hợp khác và 
vì thế là một ứng cử viên tốt cho đối số mặc định. Một cách dùng các đối số ít 
phù hợp có thể là: 
int Power (int base, unsigned int exponent = 1); 
Bởi vì 1 (hoặc bất kỳ giá trị nào khác) thì không chắc xảy ra thường xuyên 
trong tình huống này. 
 Để tránh mơ hồ, tất cả đối số mặc định phải là các đối số theo đuôi. Vì 
thế khai báo sau là không theo luật: 
void Error (char *message = "Bomb", int severity); // Trái qui tắc 
 Một đối số mặc định không nhất thiết là một hằng. Các biểu thức tùy ý có 
thể được sử dụng miễn là các biến được dùng trong các biểu thức là có sẵn 
cho phạm vi định nghĩa hàm (ví dụ, các biến toàn cục). 
 Qui ước được chấp nhận dành cho các đối số mặc định là chỉ định chúng 
trong các khai báo hàm chứ không ở trong định nghĩa hàm. 
4.10.Đối số hàng lệnh 
Khi một chương trình được thực thi dưới một hệ điều hành (như là DOS hay 
UNIX) nó có thể nhận không hay nhiều đối số từ dòng lệnh. Các đối số này 
xuất hiện sau tên chương trình có thể thực thi và được phân cách bởi các 
khoảng trắng. Bởi vì chúng xuất hiện trên cùng hàng nơi mà các lệnh của hệ 
điều hành phát ra nên chúng được gọi là các đối số hàng lệnh. 
Chương 4: Hàm 53 
 Ví dụ như xem xét một chương trình được đặt tên là sum để in ra tổng của 
tập hợp các số được cung cấp tới nó như là các đối số hàng lệnh. Hộp thoại 
4.1 minh họa hai số được truyền như là các đối số tới hàm sum như thế nào ($ 
là dấu nhắc UNIX). 
Hộp thoại 4.1 
1 
 2 
3 
$ sum 10.4 12.5 
22.9 
$ 
 Các đối số hàng lệnh được tạo ra sẵn cho một chương trình C++ thông 
qua hàm main. Có hai cách định nghĩa một hàm main: 
int main (void); 
int main (int argc, const char* argv[]); 
Cách sau được sử dụng khi chương trình được dự tính để chấp nhận các đối 
số hàng lệnh. Tham số đầu, argc, biểu thị số các đối số được truyền tới chương 
trình (bao gồm cả tên của chính chương trình). Tham số thứ hai, argv, là một 
mảng của các hằng chuỗi đại diện cho các đối số. Ví dụ từ hàng lệnh đã cho 
trong hộp thoại 4.1, chúng ta có: 
 argc is 3
 argv[0] is "sum"
 argv[1] is "10.4"
 argv[2] is "12.5"
Danh sách 4.4 minh họa một thi công đơn giản cho chương trình tính tổng 
sum. Các chuỗi được chuyển đổi sang số thực sử dụng hàm atof được định 
nghĩa trong thư viện stdlib.h. 
Danh sách 4.4 
1 
 2 
3 
4 
5 
6 
7 
8 
9 
10 
#include 
#include 
int main (int argc, const char *argv[]) 
{ 
 double sum = 0; 
 for (int i = 1; i < argc; ++i) 
 sum += atof(argv[i]); 
 cout << sum << '\n'; 
 return 0; 
} 
Chương 4: Hàm 54 
Bài tập cuối chương 4 
4.1 Viết chương trình trong bài tập 1.1 và 3.1 sử dụng hàm. 
4.2 Chúng ta có định nghĩa của hàm Swap sau 
void Swap (int x, int y) 
{ 
 int temp = x; 
 x = y; 
 y = temp; 
} 
cho biết giá trị của x và y sau khi gọi hàm: 
x = 10; 
y = 20; 
Swap(x, y); 
4.3 Chương trình sau xuất ra kết quả gì khi được thực thi? 
#include 
char *str = "global"; 
void Print (char *str) 
{ 
 cout << str << '\n'; 
 { 
 char *str = "local"; 
 cout << str << '\n'; 
 cout << ::str << '\n'; 
 } 
 cout << str << '\n'; 
} 
int main (void) 
{ 
 Print("Parameter"); 
 return 0; 
} 
4.4 Viết hàm xuất ra tất cả các số nguyên tố từ 2 đến n (n là số nguyên dương): 
void Primes (unsigned int n); 
Một số là số nguyên tố nếu như nó chỉ chia hết cho chính nó và 1. 
4.5 Định nghĩa một bảng liệt kê gọi là Month cho tất cả các tháng trong năm và sử 
dụng nó để định nghĩa một hàm nhận một tháng như là một đối số và trả về 
nó như là một hằng chuỗi. 
4.6 Định nghĩa một hàm inline IsAlpha, hàm trả về khác 0 khi tham số của nó là 
một ký tự và trả về 0 trong các trường hợp khác. 
Chương 4: Hàm 55 
4.7 Định nghĩa một phiên bản đệ qui của hàm Power đã được trình bày trong 
chương này. 
4.8 Viết một hàm trả về tổng của một danh sách các giá trị thực 
double Sum (int n, double val ...); 
trong đó n biểu thị số lượng các giá trị trong danh sách. 
Chương 4: Hàm 56 

File đính kèm:

  • pdfC++Chuong_04.pdf