Con Trỏ Hàm Trong C

     
Chào chúng ta đang theo dõi khóa huấn luyện và đào tạo lập trình trực tuyến ngôn ngữ C++.

Bạn đang xem: Con trỏ hàm trong c

Tiếp tục tò mò về bé trỏ trong ngôn từ lập trình C++, trong bài học này, mình sẽ reviews đến các bạn một loại nhỏ trỏ new có công dụng khá đặc biệt.

Như bọn họ đã biết, con trỏ có tính năng lưu trữ add của một vùng lưu giữ nào đó trên bộ lưu trữ ảo. Tuy nhiên, bộ nhớ lưu trữ ảo được chia thành nhiều phân vùng không giống nhau.


*

Như vào hình, hầu hết cục bộ phân vùng của bộ lưu trữ ảo đều dùng làm lưu trữ tài liệu (biến đơn, giá bán trị, chuỗi kí tự, ...). Nhưng dữ liệu lưu trên bộ lưu trữ ảo là dòng được sản xuất ra sau khi chương trình nào đó được thực thi với nó xin cấp phát vùng ghi nhớ trên bộ nhớ lưu trữ ảo để sử dụng. Trước đó, chương trình hoạt động dựa trên những dòng lệnh nhưng lập trình viên chuyển ra sử dụng cú pháp của ngôn từ lập trình làm sao đó. Và trước khi chạy chương trình, mã nguồn (đã được biên dịch thành mã máy) đang lưu trữ trong máy tính xách tay (có thể là bên trên ổ cứng) cũng đề xuất được hệ quản lý và điều hành load lên RAM và quản lý bằng phương pháp đưa vào bộ nhớ lưu trữ ảo. Vậy mã nguồn của chương trình sẽ lưu nơi đâu trên bộ nhớ ảo? Đó là tại phân vùng Text (Text segment) hay nói một cách khác là phân vùng Code (Code segment). Tất cả các lệnh, các hàm... Của chương trình sẽ được đưa vào phân vùng này, trong những số ấy có cả hàm main nếu chính là chương trình C++. Như chúng ta đã biết, một công tác C++ sẽ sở hữu duy độc nhất vô nhị một hàm main nhập vai trò là điểm bắt đầu của công tác đó. Như vậy, sau khoản thời gian được load mã nguồn C++ đã làm được biên dịch lên bộ lưu trữ ảo, hệ điều hành sẽ tìm đến vị trí (địa chỉ) của hàm main và chuyển mã nguồn đến mang đến CPU xử lý.

Mình hoàn toàn có thể cho chúng ta xem showroom của hàm main của một công tác C++ ngơi nghỉ trên máy mình như hình mặt dưới:

*

Như các bạn thấy, hàm main (hay bất kỳ hàm nào không giống trong chương trình) tất cả một showroom xác định trên bộ nhớ ảo. Vì chưng đó, chúng ta cũng có thể sử dụng nhỏ trỏ để trỏ đến địa chỉ của hàm main. Mặc dù nhiên, bọn họ cần lưu ý đến kiểu dữ liệu khai báo cho bé trỏ đề xuất tương yêu thích với kiểu tài liệu của vùng nhớ. Ví dụ, nhỏ trỏ dạng hình int dùng làm trỏ mang lại vùng nhớ thứ hạng int, nhỏ trỏ trỏ cho hằng (Pointer lớn const) dùng làm trỏ đến vùng lưu giữ hằng... Và để trỏ đến địa chỉ của một hàm, họ cần sử dụng con trỏ hàm (Function pointer hoặc rất có thể gọi là Pointer to lớn function).

Function pointers

Khi nhìn vào một hàm (function), ví dụ:

int foo()return 0;Chúng ta nói theo một cách khác hàm này có định danh là foo, hình dáng trả về là int, hàm foo không sở hữu và nhận đối số. Đó là phần nhiều gì bọn họ thấy được trong quy trình biên soạn mã mối cung cấp chương trình. Trực thuộc tính add của hàm chỉ được hiện ra khi chương trình đã được chạy.

int foo() // code of foo start at memory address 0x01001492return 0;int main()int n = foo();return 0;Như vậy, lúc trong hàm main chạy đến cái lệnh call hàm foo, hệ quản lý sẽ tìm kiếm đến địa chỉ cửa hàng của hàm foo trên bộ nhớ lưu trữ ảo và gửi mã lệnh của hàm foo mang lại CPU liên tiếp xử lý. Để in ra địa chỉ cửa hàng của hàm foo, chúng ta có thể làm như sau:

int foo()return 0;int main()cout << foo << endl;return 0;Kết quả:

013D1492Như các bạn thấy, lúc muốn thực thi một hàm, họ cần thêm cặp lốt ngoặc nhằm truyền đối số vào mang đến hàm (nếu hàm không tồn tại tham số thì để trống). Nếu bọn họ không áp dụng cặp dấu ngoặc, sử dụng tên hàm trả về add của hàm trên bộ nhớ lưu trữ ảo. Và showroom này hoàn toàn có thể được gán con một bé trỏ có kiểu dữ liệu tương xứng (function pointer).

Function pointers syntax

Cú pháp của một nhỏ trỏ hàm có tương đối nhiều điểm khác biệt so với cách khai báo con trỏ thông thường.

(*)( );Mình rước ví dụ, nhằm trỏ mang lại hàm foo trong lấy ví dụ như trên, chúng ta cần khai báo con trỏ hàm như sau:

int (*pFoo) ();Trong đó, int là đẳng cấp trả về của hàm foo, pFoo là tên gọi của con trỏ, và hàm foo không có tham số bắt buộc phần trong ngoặc mình bỏ trống. Một lấy ví dụ như khác, mình tất cả hàm như bên dưới:

void swapValue(int &value1, int &value2) int temp = value1;value1 = value2;value2 = temp;Hàm swapValue có không có kiểu trả về, với nó nhận vào 2 tham số đều có kiểu tham chiếu int. Như vậy, mình rất có thể khai báo một nhỏ trỏ hàm dùng để trỏ mang đến hàm swapValue như sau:

void(*pSwap) (int &, int &);Gán địa chỉ của hàm mang lại Function pointersSau khi đã chiếm lĩnh con trỏ hàm được khai báo tương ứng với hàm, chúng ta cũng có thể gán add của hàm mang lại chúng:

void swapValue(int &value1, int &value2) int temp = value1;value1 = value2;value2 = temp;int main()void(*pSwap) (int &, int &) = swapValue;cout << pSwap << endl;cout << swapValue << endl;return 0;Lưu ý, khi phải lấy địa chỉ của hàm, họ chỉ thực hiện duy độc nhất tên hàm, không đặt thêm cặp vệt ngoặc vào.

Chỉ gồm con trỏ được khai báo tất cả kiểu tài liệu trả về và list tham số tương xứng mới trỏ mang đến hàm được.

// function prototypesint foo();double goo();int hoo(int x); // function pointer assignmentsint (*funcPtr1)() = foo; // okayint (*funcPtr2)() = goo; // wrong -- return types don"t match!double (*funcPtr4)() = goo; // okayfuncPtr1 = hoo; // wrong -- fcnPtr1 has no parameters, but hoo() doesint (*funcPtr3)(int) = hoo; // okaySử dụng Function pointersSau khi đã nắm giữ được địa chỉ của hàm, con trỏ hàm rất có thể được áp dụng như hàm trải qua toán tử dereference. Ví dụ:

void swapValue(int &value1, int &value2)int temp = value1;value1 = value2;value2 = temp;int main()void(*pSwap) (int &, int &) = swapValue;int a = 1, b = 5;cout << "Before: " << a << " " << b << endl;(*pSwap)(a, b);cout << "After: " << a << " " << b << endl;return 0;Lưu ý, tham số khoác định của hàm không vận dụng được cho nhỏ trỏ hàm, vày tham số mang định được compiler xác minh tại thời gian compile chương trình, còn bé trỏ hàm được áp dụng tại thời gian chương trình đã chạy.

Xem thêm: Bộ Đề Thi Học Sinh Giỏi Lớp 3 Môn Toán 3 Chọn Lọc Năm Học 2020

Sử dụng bé trỏ hàm làm cho tham số

Một con trỏ hàm cũng là một trong biến bé trỏ, do đó chúng ta cũng có thể sử dụng con trỏ hàm là thông số của một hàm làm sao đó. Lúc tham số của hàm là con trỏ hàm, bọn họ sẽ truyền đối số là địa chỉ cửa hàng của một hàm. Hàm được sử dụng làm đối số của hàm hoàn toàn có thể gọi là callback function.

Mình lấy ví dụ về hàm selectionSort dùng làm sắp xếp dữ liệu trong mảng số nguyên theo máy tự tăng dần:

#include // use for std::swapvoid selectionSort(int *arr, int length)for (int i_start = 0; i_start < length; i_start++)int minIndex = i_start;for (int i_current = i_start + 1; i_current < length; i_current++)if (arr > arr)minIndex = i_current;swap(arr, arr); // std::swap//................int main()int arr<> = 1, 4, 2, 3, 6, 5, 8, 9, 7 ;int length = sizeof(arr) / sizeof(int);cout << "Before sorted: ";printArray(arr, length);selectionSort(arr, length);cout << "After sorted: ";printArray(arr, length);return 0;Kết quả:

Before sorted: 1 4 2 3 6 5 8 9 7After sorted: 1 2 3 4 5 6 7 8 9Bây giờ đề ra trường hợp bọn họ muốn có bố trí mảng một chiều thực hiện thuật toán selectionSort nhưng thu xếp theo sản phẩm tự giảm dần. Như vậy, bọn họ cần mang lại 2 hàm selectionSort để thỏa mãn nhu cầu cho 2 trường thích hợp mình kể trên. Trong lúc cả 2 hàm selectionSort này chỉ khác nhau về toán tử so sánh tại phép so sánh ở mệnh đề if trong tầm lặp.

Để xử lý vấn đề này, thứ nhất mình cần có 2 hàm thực hiện quá trình so sánh như sau:

bool ascending(int left, int right)return left > right;bool descending(int left, int right)return left < right;Hai hàm này chỉ có công dụng thay cố kỉnh phép đối chiếu trong mệnh đề if. Nếu chúng ta cần thu xếp giá trị vào mảng theo đồ vật tự tăng dần, chúng ta sẽ thay thế sửa chữa hàm ascending vào mệnh đề if trong hàm selectionSort như sau:

void selectionSort(int *arr, int length)for (int i_start = 0; i_start < length; i_start++)int minIndex = i_start;for (int i_current = i_start + 1; i_current < length; i_current++)if (ascending(arr, arr)) // replace comparison expression by ascending functionminIndex = i_current;swap(arr, arr); // std::swapLúc này, họ vẫn còn yêu cầu thêm một hàm selectionSort không giống và sửa chữa biểu thức so sánh trong mệnh đề if bởi hàm descending để rất có thể sắp xếp mảng theo chiều bớt dần. Bọn họ cần thiết kế lại hàm selectionSort này sao cho tất cả những người dùng rất có thể tùy chọn việc bố trí theo trang bị tự tăng đột biến hay bớt dần theo từng thời gian khác nhau. Họ sẽ cung ứng tham số thiết bị 3 đến hàm selectionSort là một trong con trỏ hàm dùng để làm trỏ cho hàm ascending hoặc descending tùy thuộc vào lời điện thoại tư vấn hàm selectionSort. Do hàm ascending cùng descending có cấu trúc kiểu trả về với tham số hoàn toàn giống nhau, nên bạn cũng có thể sử dụng phổ biến một kiểu bé trỏ hàm. Bản thân định nghĩa nhỏ trỏ hàm cần sử dụng làm tham số sản phẩm 3 của hàm selectionSort như sau:

bool (*comparisonFunc)(int, int);Bây giờ, bọn họ sẽ sửa lại hàm selectionSort thành phiên bạn dạng sử dụng con trỏ hàm:

bool ascending(int left, int right)return left > right;bool descending(int left, int right)return left < right;void selectionSort(int *arr, int length, bool (*comparisonFunc)(int, int))for (int i_start = 0; i_start < length; i_start++)int minIndex = i_start;for (int i_current = i_start + 1; i_current < length; i_current++)if (comparisonFunc(arr, arr)) // use function pointer as ascending or descending functionminIndex = i_current;swap(arr, arr); // std::swapLúc này, đưa sử mình muốn sắp xếp mảng theo sản phẩm công nghệ tự bớt dần, mình sẽ sử dụng lời hotline hàm như sau:

int main()int arr<> = 1, 4, 2, 3, 6, 5, 8, 9, 7 ;int length = sizeof(arr) / sizeof(int);cout << "Before sorted: ";printArray(arr, length);selectionSort(arr, length, descending);cout << "After sorted: ";printArray(arr, length);return 0;Mình sử dụng địa chỉ của hàm descending làm cho đối số mang đến tham số máy 3 của hàm selectionSort. Như vậy, hàm descending sẽ được sử dụng phía bên trong hàm selectionSort và mảng của bọn họ sẽ thu xếp theo lắp thêm tự giảm dần.

Before sorted: 1 4 2 3 6 5 8 9 7After sorted: 9 8 7 6 5 4 3 2 1Nếu ý muốn đổi trái lại thứ từ của mảng khi sắp đến xếp, chúng ta chỉ đề nghị thay đối số đồ vật 3 của hàm selectionSort là địa chỉ của hàm ascending:

int main()int arr<> = 1, 4, 2, 3, 6, 5, 8, 9, 7 ;int length = sizeof(arr) / sizeof(int);cout << "Before sorted: ";printArray(arr, length);selectionSort(arr, length, ascending);cout << "After sorted: ";printArray(arr, length);return 0;Với bài toán đặt thêm tham số thứ 3 của hàm selectionSort là một trong những con trỏ hàm, chúng ta cũng có thể thiết kế thêm những tùy chọn cho điều kiện sắp xếp mảng một chiều khác nhau và vẫn hoàn toàn có thể sử dụng mang đến hàm selectionSort. Ví dụ mình muốn thêm một kiểu sắp xếp có điều kiện khác là các số chẵn trong mảng sẽ đứng trước, những số lẻ trong mảng sẽ đứng sau, cùng phần chẵn xuất xắc phần lẻ hầu hết được thu xếp tăng dần, mình sẽ làm như sau:

bool evensFirst(int left, int right)//if left is even & right is odd, not need to lớn swapif ((left % 2 == 0) && (right % 2 != 0))return false;//if left is odd và right is even, swap this coupleif ((left % 2 != 0) && (right % 2 == 0))return true;return ascending(left, right);Và mình chỉ cần sử dụng địa chỉ cửa hàng của hàm này làm cho đối số:

int main()int arr<> = 1, 4, 2, 3, 6, 5, 8, 9, 7 ;int length = sizeof(arr) / sizeof(int);cout << "Before sorted: ";printArray(arr, length);selectionSort(arr, length, evensFirst);cout << "After sorted: ";printArray(arr, length);return 0;Kết quả:

Before sorted: 1 4 2 3 6 5 8 9 7After sorted: 2 4 6 8 1 3 5 7 9Như các bạn thấy, sử dụng con trỏ hàm vào trường thích hợp này đem về cho chúng ta một cách thực hiện hàm sắp tới xếp công dụng hơn.

Tham số khoác định của tham số bé trỏ hàm

Chúng ta hoàn toàn có thể cung cấp cho cho tham số con trỏ hàm một add hàm cầm cố thể, và hàm đó sẽ tiến hành gọi mặc định nếu bọn họ không cung cấp đối số mang lại tham số bé trỏ hàm. Ví dụ:

void selectionSort(int *arr, int length, bool (*comparisonFunc)(int, int) = ascending)for (int i_start = 0; i_start < length; i_start++)int minIndex = i_start;for (int i_current = i_start + 1; i_current < length; i_current++)if (comparisonFunc(arr, arr)) // use function pointer as ascending or descending functionminIndex = i_current;swap(arr, arr); // std::swapNhư vậy, lời hotline hàm selectionSort với 2 đối số sẽ được mặc định là sắp xếp mảng tăng dần.

std::function in C++11

Chuẩn C++11 hỗ trợ cho bọn họ một cách sửa chữa thay thế cho việc sử dụng con trỏ hàm bằng phương pháp sử dụng kiểu dữ liệu function trực thuộc thư viện functional. Thư viện này cũng rất được định nghĩa trong namespace std nên bọn họ cần có dòng lệnh using namespace std; hoặc hoàn toàn có thể khai báo là std::function.

Cú pháp khai báo trở thành kiểu std::function như sau:

std::function< () > varName;Ví dụ:

std::function< bool(int, int) > comparisonFunc;Mình rước ví dụ về việc áp dụng kiểu dữ liệu std::function như sau:

#include #include using namespace std;void addOne(int &value)value++;int main()function func = addOne;int number = 5;func(number);cout << "New value: " << number << endl;return 0;Như chúng ta thấy, thực hiện kiểu tài liệu std::function cũng tương tự như thực hiện con trỏ hàm, chỉ khác biệt về bí quyết khai báo.

Tổng kết

Con trỏ hàm (function pointers) thường xuyên được áp dụng khi bọn họ có những hàm tất cả cùng kiểu trả về và danh sách tham số. Đặt bé trỏ hàm làm tham số của hàm cũng là 1 trong cách áp dụng con trỏ hàm tương đối phổ biến.

Vì nhỏ trỏ hàm gồm cú pháp khai báo khó khăn nhớ hơn hình trạng std::function, bản thân khuyến khích chúng ta sử dụng loại std::function được định nghĩa bên trong thư viện functional của chuẩn chỉnh C++11.

Bài tập cơ bản

1/ Sửa lại hàm selectionSort phiên bạn dạng sử dụng nhỏ trỏ hàm làm cho tham số đồ vật 3 sao cho cân xứng với giải pháp định nghĩa của 2 hàm đối chiếu bên dưới:

bool ascending(int left, int right)return left < right;bool descending(int left, int right)return left > right;2/ thực hiện con trỏ hàm để tạo hàm rất có thể thực hiện 4 phép toán cơ bạn dạng (+, -, *, /). Biết rằng, với mỗi phép toán chúng ta có một hàm gồm kiểu trả về float, từng hàm gồm 2 tham số giao diện float. Sau đó, viết hoàn thành xong một chương trình 1-1 giản được cho phép người sử dụng nhập vào toán tử (+, -, *, /) từ bàn phím và tiến hành phép toán cùng với toán tử tương ứng trải qua hàm mà bạn đã định nghĩa.

Xem thêm: Làm Thế Nào Để Vô Hiệu Hóa Cảm Biến Tiệm Cận ? ▷ ➡️ Vidabytes

Hẹn chạm chán lại chúng ta trong bài bác học tiếp theo sau trong khóa đào tạo lập trình C++ phía thực hành.