Lập trình C - Con trỏ hàm

Con trỏ hàm là gì?

Con trỏ hàm là một biến lưu trữ địa chỉ của một hàm, thông qua biến đó, ta có thể gọi hàm mà nó trỏ tới. Điều này rất thuận tiện khi bạn muốn định nghĩa các chức năng khác nhau cho một nhóm các đối tượng khá giống nhau. (Như ví dụ về Button ở trên).

Sử dụng con trỏ hàm như thế nào?

Một con trỏ hàm có thể khởi tạo theo mẫu sau:

<kiểu trả về> (*<tên con trỏ>)(<danh sách đối số>);

Ví dụ về con trỏ hàm nhận vào một biến kiểu int và trả về dữ liệu kiểu void.

void (*func)(int);

Nhưng đây mới chỉ là khai báo, cũng giống như mọi con trỏ khác, con trỏ hàm phải được định nghĩa giá trị trước khi sử dụng, nhưng chúng ta không thể dùng từ khóa new hay malloc để cấp phát vùng nhớ cho một con trỏ hàm, vì như thế thì chẳng cách nào định nghĩa được các lệnh của nó, hơn nữa vùng nhớ của lệnh và của biến cũng khác nhau vậy chỉ còn một cách là cho nó trỏ đến một vùng nhớ lưu trữ giá trị khai báo sẵn.
Về bản chất, máy chỉ hiểu được các lệnh mã máy với 0 và 1, nên không chỉ có biến hay con trỏ, các câu lệnh cũng có địa chỉ của riêng nó.

Cũng nhưng những con trỏ thông thường, chúng ta có thể truyền địa chỉ của một hàm vào con trỏ với cách đơn giản như sau:

void thefunc(int a)
{
	// DO SOMETHING
}
int main()
{
	// ...
	void (*func)(int);
	func = &thefunc;
	// ...
}

Và để gọi một hàm đã lưu trữ trong con trỏ hàm, ta chỉ cần gọi con trỏ với một danh sách đối số phù hợp:

func(1); 
// OR
(*func)(1);

Lưu ý là con trỏ hàm và hàm được trỏ đến phải có cùng danh sách đối số và kiểu trả về.

Truyền hàm vào hàm

Do con trỏ hàm được khai báo dựa theo kiểu trả về và danh sách đối số của hàm sẽ được trỏ đến nên để thuận tiện hơn trong các thao tác xử lý, ta nên định kiểu cho mỗi kiểu con trỏ hàm để nó có một cái tên nhất định (thông qua từ khóa typedef). Chẳng hạn như:

typedef int (*Func_int)();

Vậy là ta đã có thể sử dụng kiểu dữ liệu Func_int như một kiểu dữ liệu định nghĩa các con trỏ hàm không đối số trả về một phần tử kiểu int.

Theo đó, ta có thể yêu cầu một đối số func_int từ một hàm, như sau:

void foo(func_int fun, int a)
{
	// DO SOMETHING
	// ...
}

Và thao tác truyền hàm vào hàm được thực hiện như sau:

typedef int (*func_int)();
 
void foo(func_int fun, int a)
{
	// DO SOMETHING
	// ...
}
 
int doSomething()
{
	// DO SOMETHING
	// ...
}
 
int main()
{
	// ...
	foo(&doSomething, 0);
	// ...
}