Một cách ngắn gọn: Con trỏ hàm là biến lưu địa chỉ của hàm.
Nếu bạn tự hỏi học con trỏ hàm để làm gì thì mình sẽ cho bạn một lý do để học con trỏ hàm: một ứng dụng phổ biến của con trỏ hàm là để pass sự kiện từ lớp dưới lên các lớp trên. Lấy ví dụ khi bạn click chuột, sự kiện này được gửi từ chuột lên hệ điều hành thông qua giao thức USB, hệ điều hành sẽ gửi sự kiện này vào application thông qua con trỏ hàm. Để hệ điều hành biết hàm cần chạy ở đâu, application sẽ viết một hàm bình thường, sau đó đưa con trỏ trỏ tới hàm đó cho hệ điều hành và kêu với hệ điều hành: hãy nhảy đến vị trí đó và chạy hàm nếu có sự kiện click chuột xảy ra. Đó là nguyên lý để hiểu tại sao bạn có những hàm void
Chú ý: nếu đọc không hiểu thì bỏ qua.
1. Khai báo biến con trỏ hàm:
Nhận xét:
Để sử dụng con trỏ cần có các toán tử sau:
Nhận xét:
3. Typedef
Ta rất nên dùng typdef để định nghĩa một function pointer, cái này sẽ giúp code clean hơn.
Ví dụ:
Giải thích:
Bài tiếp: [C] Function Pointer (Con trỏ hàm) - P2
Nếu bạn tự hỏi học con trỏ hàm để làm gì thì mình sẽ cho bạn một lý do để học con trỏ hàm: một ứng dụng phổ biến của con trỏ hàm là để pass sự kiện từ lớp dưới lên các lớp trên. Lấy ví dụ khi bạn click chuột, sự kiện này được gửi từ chuột lên hệ điều hành thông qua giao thức USB, hệ điều hành sẽ gửi sự kiện này vào application thông qua con trỏ hàm. Để hệ điều hành biết hàm cần chạy ở đâu, application sẽ viết một hàm bình thường, sau đó đưa con trỏ trỏ tới hàm đó cho hệ điều hành và kêu với hệ điều hành: hãy nhảy đến vị trí đó và chạy hàm nếu có sự kiện click chuột xảy ra. Đó là nguyên lý để hiểu tại sao bạn có những hàm void
HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
trong STM32, private void button1_Click(object sender, System.EventArgs e)
trong lập trình C#, setOnClickListener( new OnClickListener() { ... };
trong Java hay onclick="myFunction()"
trong JavaScript. Với các ngôn ngữ bậc cao, khái niệm con trỏ hàm được thay thế bằng những khái niệm khác để dễ hiểu hơn nhưng bản chất bên trong vẫn là sử dụng con trỏ hàm.Chú ý: nếu đọc không hiểu thì bỏ qua.
1. Khai báo biến con trỏ hàm:
<kiểu dữ liệu trả về của hàm> ( *<tên con trỏ hàm>) (<kiểu tham số truyền vào số 0>, <kiểu tham số truyền vào số 1>, ...);
Ví dụ:
C:
void (*fun_ptr)(int); // declare function pointer : fun_ptr
float (*fun1_ptr)(int,float); // declare function pointer : fun1_ptr
uint32_t (*fun2_ptr)(int, float, uint32_t); // declare function pointer : fun2_ptr
- Biến
fun_ptr
có kiểu dữ liệuvoid (*)(int)
- Biến
fun1_ptr
có kiểu dữ liệufloat (*)(int,float)
- Biến
fun2_ptr
có kiểu dữ liệuuint32_t (*)(int, float, uint32_t)
Để sử dụng con trỏ cần có các toán tử sau:
* : toán tử đi đến vị trí lưu trong con trỏ và chạy hàm
& : toán tử lấy địa chỉ hàm
Ví dụ:
C:
#include <stdio.h>
#include <stdint.h>
void fun0(int a)
{
printf("fun0: a=%d\n", a);
}
float fun1(int a, float b)
{
printf("fun1: a=%d b=%f\n", a, b);
return b;
}
uint32_t fun2(int a, float b, uint32_t c)
{
printf("fun2: a=%d b=%f c=0x%08X\n", a, b, c);
return c;
}
void fun3(int a)
{
printf("fun3: a=%d\n", a);
}
int main()
{
void (*fun_ptr)(int); // declare function pointer : fun_ptr
fun_ptr = &fun0; // assign value of fun_ptr with address of fun0
(*fun_ptr)(10); // go to pointed address and run code with parameter
float (*fun1_ptr)(int, float) = &fun1; // declare function pointer: fun1_ptr and assign value of fun1_ptr with address of fun1
float b = (*fun1_ptr)(10, 20.5); // go to pointed address, run code with parameter and get return value
printf("fun1_ptr return value b=%f \n", b);
uint32_t(*fun2_ptr)(int, float, uint32_t); // declare function pointer: fun2_ptr
fun2_ptr = &fun2; // assign value of fun2_ptr with address of fun2
uint32_t c = (*fun2_ptr)(10, 20.5, (uint32_t)&fun2); // go to pointed address, run code with parameter and get return value
printf("fun2_ptr return value c=%d \n", c);
fun_ptr = &fun3; // change value of fun_ptr from address of fun0 to address of fun3
(*fun_ptr)(100); // go to pointed address, run code with parameter
return 0;
}
fun0: a=10
fun1: a=10 b=20.500000
fun1_ptr return value b=20.500000
fun2: a=10 b=20.500000 c=0x000A13C0
fun2_ptr return value c=660416
fun3: a=100
fun1: a=10 b=20.500000
fun1_ptr return value b=20.500000
fun2: a=10 b=20.500000 c=0x000A13C0
fun2_ptr return value c=660416
fun3: a=100
- Ta thấy được sự tương quan giữa: khai báo biến với khai báo hàm, lấy địa chỉ hàm với lấy địa chỉ biến, lấy giá trị biến với chạy hàm để lấy giá trị return.
- Có thể coi con trỏ biến là một con trỏ hàm đặc biệt mà việc gọi con trỏ biến (bằng toán tử *) là việc gọi một hàm với 0 tham số truyền vào, không thực hiện lệnh nào mà chỉ trả về giá trị mà con trỏ trỏ tới.
Ta rất nên dùng typdef để định nghĩa một function pointer, cái này sẽ giúp code clean hơn.
Ví dụ:
C:
#include <stdio.h>
#include <stdint.h>
typedef int (*func_t)(int, int);
int add(int a, int b)
{
return a + b;
}
int minus(int a, int b) {
return a - b;
}
int main()
{
func_t func;
func = &add;
int sum = (*func)(100, 200);
printf("sum = %d \n", sum);
func = −
int sub = (*func)(100, 200);
printf("sub = %d \n", sub);
return 0;
}
sum = 300
sub = -100
sub = -100
- Trong ví dụ trên, lệnh
typedef int (*func_t)(int, int);
đã đặt tên cho kiểu dữint (*)(int,int)
làfunc_t
. - Do đó khi ta khai báo
func_t func;
thì thực chất là đang khai báo biếnfunc
có kiểu dữ liệuint (*)(int,int)
- Hai hàm số
int add(int a, int b)
vàint minus(int a, int b)
đều có dạngint (*)(int,int)
nên ta có thể gánfunc = &add;
vàfunc = −
Bài tiếp: [C] Function Pointer (Con trỏ hàm) - P2
Last edited: