I. Chia chương trình thành từng hàm
1. Kỹ thuật lập trình hướng cấu trúc là gì?
Cùng xem xét một ví dụ: Cho ba số nguyên dương a, b, c không nhỏ hơn 2. Chúng ta muốn kiểm tra xem những số nào trong ba số đó là số nguyên tố và hiển thị thông báo tương ứng.
Chúng ta biết rằng một số nguyên tố không chia hết cho bất kỳ số nào ngoài 1 và chính nó. Dựa trên kiến thức từ các bài học trước, chúng ta có thể xây dựng một chương trình như sau:
Biên dịch và chạy chương trình trên với a=2,b=5,c=9 sẽ cho ra kết quả:
Chúng ta đã nhận thấy rằng quá trình kiểm tra số nguyên tố cho từng số là tương tự nhau, chỉ khác nhau về giá trị số. Việc phải viết lại cùng một đoạn mã ba lần làm cho chương trình trở nên dài dòng. Tình huống này còn trở nên tồi tệ hơn nếu chúng ta cần kiểm tra nhiều số như 4, 5, ..., 1000.
Thực tế, lập trình là quá trình xây dựng một giải thuật để giải quyết các bài toán, với mỗi bước xử lý một công việc cụ thể. Trong nhiều trường hợp, chúng ta sẽ gặp các công việc tương tự nhau. Để tránh việc phải viết mã lặp lại nhiều lần, gây lãng phí thời gian, chúng ta sẽ chia chương trình thành các hàm con. Mỗi hàm sẽ đảm nhận một công việc cụ thể và tất cả các hàm này sẽ được gọi từ một hàm chính. Phương pháp lập trình như vậy được gọi là lập trình hướng cấu trúc hoặc lập trình theo module. Ưu điểm của phương pháp này bao gồm:
- Tư duy giải thuật rõ ràng.
- Chương trình đơn giản và dễ hiểu.
- Có thể tái sử dụng mã code ở nhiều vị trí trong chương trình mà không cần viết lại nhiều lần.
- Dễ dàng theo dõi, kiểm tra và chỉnh sửa giải thuật.
Phương pháp lập trình này rất phù hợp trong lập trình thi đấu, vì mỗi giải thuật thường chỉ áp dụng cho một số bài toán cụ thể. Tất nhiên, phương pháp này cũng có nhược điểm, nhưng chúng ta sẽ không bàn luận đến trong bài học này.
2. Khái niệm về hàm. Hàm tự định nghĩa và hàm dựng sẵn là gì?
C++ là một ngôn ngữ lập trình hướng cấu trúc, bao gồm một hàm main() và các hàm con khác trong chương trình. Hàm là một khối lệnh được sử dụng để xử lý một phần công việc cụ thể trong chương trình.
Có hai loại hàm trong C++ là hàm tự định nghĩa và hàm dựng sẵn được cung cấp bởi các thư viện của C++. Sử dụng các hàm một cách linh hoạt sẽ hỗ trợ rất tốt cho lập trình viên trong quá trình xây dựng chương trình.
II. Các hàm toán học dựng sẵn trong C++ là gì?
Trong thư viện chuẩn của C++, có nhiều hàm toán học được cung cấp để hỗ trợ người lập trình. Để sử dụng các hàm toán học trong C++, trước tiên chúng ta cần khai báo thư viện và không gian tên chứa chúng:
Bảng dưới đây là một số hàm thường dùng trong quá trình làm việc với C++:
III. Các bước định nghĩa và cách sử dụng một hàm là gì?
1. Khai báo hàm
giống như biến và hằng số, một hàm cũng cần được khai báo và định nghĩa trước khi sử dụng. Khai báo hàm có thể được đặt ở bất kỳ đâu trong chương trình. Cú pháp khai báo một hàm như sau:
Trong đó ta có:
- {Kiểu_trả_về}: Mỗi hàm trong C++ phải trả về một giá trị. Kiểu_trả_về là kiểu dữ liệu của giá trị mà hàm sẽ trả về. Ngoài ra, trong trường hợp một hàm không trả về giá trị, chúng ta sử dụng kiểu_trả_về là void.
- {Tên_hàm}: Đây là tên mà người lập trình đặt cho hàm. Quy ước đặt tên theo convention của C++ là snake_case và tránh trùng với các từ khóa của ngôn ngữ.
- {Danh_sách_tham số}: Khi sử dụng một hàm để thực hiện một công việc cụ thể, ta cần cung cấp dữ liệu đầu vào cho hàm. Danh sách tham số là danh sách các biến mà hàm sẽ sử dụng để tham chiếu đến dữ liệu đầu vào. Danh sách tham số không bắt buộc phải có và có thể có nhiều tham số khác nhau.
Ví dụ, dưới đây là một vài khai báo hàm hợp lệ:
2. Định nghĩa hàm
Phần định nghĩa hàm là phần quan trọng nhất của một hàm, nó quyết định hàm đó sẽ làm công việc gì và trả ra kết quả là gì. Cú pháp định nghĩa hàm như sau:
phần {Thân_hàm} của một hàm chứa các câu lệnh thực hiện công việc của hàm. Đối với hàm có {Kiểu_trả_về} là một kiểu dữ liệu nào đó, cần có ít nhất một câu lệnh return {Giá_trị_trả_về}; trong phần {Thân_hàm} để trả về giá trị tương ứng. Trong trường hợp hàm có {Kiểu_trả_về} là void, không cần và cũng không thể sử dụng câu lệnh return để trả về giá trị.
Phần định nghĩa hàm có thể được đặt ngay dưới phần khai báo hàm hoặc sau khi đã khai báo hàm và nằm ở một vị trí bất kỳ trong chương trình.
Ví dụ: Hàm dưới đây trả về giá trị lớn nhất giữa hai số nguyên a và b:
3. Lời gọi hàm
Một hàm sau khi đã được định nghĩa cần được gọi ra để hoạt động. Hàm có thể được gọi trực tiếp trong hàm main() hoặc thông qua việc gọi từ một hàm khác và hàm đó được gọi trong hàm main().
Cách gọi hàm phụ thuộc vào kiểu trả về của hàm ({Kiểu_trả_về}). Nếu kiểu trả về là một kiểu dữ liệu (tức là hàm trả về giá trị), thì lời gọi hàm có thể được sử dụng trong các câu lệnh gán, biểu thức tính toán hoặc biểu thức logic. Ngược lại, nếu kiểu trả về là void, thì hàm chỉ được gọi đơn lẻ mà không được sử dụng trong biểu thức hoặc gán giá trị.
Ví dụ 1: Hàm int max_value(int a, int b)
được gọi ra trong chương trình chính để tìm giá trị lớn nhất của 3 cặp số khác nhau:
Kết quả chạy chương trình với x1=1,y1=2,x2=5,y2=4,x3=10,y3=10 là:
Trong ví dụ trên, chúng ta cần tìm giá trị lớn nhất giữa ba cặp số (x₁, y₁), (x₂, y₂) và (x₃, y₃), trong đó các cặp số khác nhau. Nếu không sử dụng hàm, chúng ta sẽ phải viết lại quy trình tìm giá trị lớn nhất ba lần, mỗi lần kiểm tra một cặp số. Tuy nhiên, nếu sử dụng hàm, chúng ta chỉ cần viết một lần quy trình kiểm tra và định nghĩa một hàm. Sau đó, ta có thể gọi hàm đó ba lần với các tham số tương ứng là các cặp số cần kiểm tra. Điều này giúp tiết kiệm thời gian và tận dụng được lợi ích của việc sử dụng hàm trong chương trình.
Ví dụ 2: Dùng hàm void sum(int a, int b)
để tính tổng hai số a và,b, sau đó lưu vào biến s. Vì kiểu của hàm là void
nên chỉ có thể gọi hàm bằng lời gọi đơn lẻ:
Kết quả chạy chương trình:
IV. Tham số của hàm là gì?
Tham số trong thực tế đại diện cho các biến mà dữ liệu được truyền vào hàm khi hàm đó được gọi. Khi dữ liệu được truyền vào hàm, nó sẽ được lưu vào các tham số và hàm sẽ sử dụng dữ liệu trên các tham số đó để thực hiện các tính toán. Tham số được chia thành hai loại: tham số thực tế và tham số hình thức.
1. Tham số thực sự
Chính là các biến, hằng ở bên ngoài truyền vào trong một hàm. Khi truyền các giá trị này vào hàm, các giá trị đó sẽ được sử dụng dưới tên của tham số hình thức. Lấy ví dụ:
Trong hàm main()
, ta thấy hai biến a và b được truyền vào hàm int sum(int a, int b);
. Ở đây, a và b chính là các tham số thực sự, vì chúng chứa dữ liệu thực tế cần thao tác.
2. Tham số hình thức
Tham số hình thức là danh sách các tham số được đặt sau khai báo của một hàm. Những tham số này đại diện cho dữ liệu mà chúng ta muốn truyền vào hàm. Nhờ có tham số, chúng ta có thể sử dụng hàm với nhiều bộ dữ liệu khác nhau mà không cần viết lại cùng một quy trình tính toán. Có hai cách để truyền tham số thực sự từ bên ngoài vào hàm:
2.1. Truyền tham trị
Với cách truyền tham số này, hàm sẽ tạo một bản sao của dữ liệu được truyền vào và sử dụng bản sao đó để thực hiện các tính toán trong hàm mà không làm thay đổi dữ liệu gốc bên ngoài. Ví dụ:
Chạy chương trình này sẽ thu được kết quả:
Chúng ta có thể thấy rằng giá trị của biến a trong hàm main() không bị thay đổi sau khi gọi hàm increase(a). Điều này xảy ra vì khi chúng ta gọi hàm increase(a), chương trình tạo ra một bản sao của biến a và truyền vào hàm increase(int x). Do đó, biến a trong hàm main() và tham số x trong hàm increase(int x) là độc lập, và việc thay đổi giá trị của x trong hàm increase(int x) không ảnh hưởng đến giá trị của a trong hàm main().
Chúng ta nên sử dụng truyền tham trị trong những trường hợp mà dữ liệu truyền vào chỉ được sử dụng để tính toán trung gian cho những kết quả khác. Điều này đồng nghĩa rằng chúng ta không cần thay đổi giá trị của dữ liệu gốc khi thực hiện tính toán trong hàm. Bằng cách truyền tham trị, chúng ta tạo ra một bản sao của dữ liệu và sử dụng bản sao đó để tính toán, mà không làm ảnh hưởng đến giá trị gốc. Việc này đảm bảo tính độc lập và an toàn cho dữ liệu ban đầu.
2.2. Truyền tham chiếu
Với truyền tham chiếu, biến truyền vào sẽ được truyền trực tiếp địa chỉ của nó vào trong hàm, nhưng với một cái tên khác, và dữ liệu được thay đổi trong hàm sẽ được cập nhật vào dữ liệu gốc đã truyền vào. Để truyền dữ liệu bằng tham chiếu, chúng ta chỉ cần thêm toán tử & phía trước tham số hình thức trong khai báo hàm. Ví dụ: