Hàm trỏ (function pointer) và trỏ hàm (pointer to function) là hai khái niệm thường bị nhầm lẫn do cách đặt tên gần giống nhau, nhưng bản chất và cách sử dụng hoàn toàn khác biệt. Trong C, việc hiểu rõ sự khác biệt này là chìa khóa để xây dựng các hệ thống linh hoạt như bảng hàm xử lý sự kiện, cơ chế callback, hoặc thiết kế mô-đun có thể mở rộng.
Hàm trỏ — biến lưu địa chỉ của một hàm
Một hàm trỏ là một biến có kiểu dữ liệu là con trỏ đến hàm, cho phép lưu trữ địa chỉ bắt đầu của một hàm đã được định nghĩa. Kiểu khai báo chuẩn có dạng:
return_type (*variable_name)(parameter_types);
Ví dụ: khai báo một hàm trỏ calc_op nhận hai số nguyên và trả về số nguyên:
#include <stdio.h>
int add(int a, int b) { return a + b; }
int mul(int a, int b) { return a * b; }
int main() {
int (*calc_op)(int, int); // Hàm trỏ
calc_op = add;
printf("Kết quả cộng: %d\n", calc_op(5, 3)); // → 8
calc_op = mul;
printf("Kết quả nhân: %d\n", calc_op(5, 3)); // → 15
return 0;
}
Trỏ hàm — hàm trả về kiểu con trỏ
Ngược lại, "trỏ hàm" không phải là một từ khóa hay khái niệm riêng — đây là cách gọi tắt cho một hàm có kiểu trả về là con trỏ. Cú pháp khai báo hàm có kiểu trả về là con trỏ như sau:
return_type* function_name(parameters);
Ví dụ: hàm allocate_buffer cấp phát vùng nhớ động và trả về con trỏ tới vùng đó:
#include <stdio.h>
#include <stdlib.h>
char* allocate_buffer(size_t size) {
char* ptr = malloc(size);
if (ptr == NULL) {
fprintf(stderr, "Cấp phát bộ nhớ thất bại.\n");
exit(EXIT_FAILURE);
}
return ptr;
}
int main() {
char* buf = allocate_buffer(64);
sprintf(buf, "Xin chào từ vùng nhớ động!");
printf("%s\n", buf);
free(buf);
return 0;
}
So sánh trực quan
- Hàm trỏ:
int (*p)(float, double)—plà biến trỏ đến hàm nhậnfloatvàdouble, trả vềint. - Trỏ hàm:
int* create_array(int n)— hàmcreate_arraytrả vềint*, tức con trỏ tới số nguyên.
Ứng dụng thực tế
Một ví dụ tổng hợp: bảng hàm toán học dùng hàm trỏ để chọn phép toán tại thời điểm chạy:
#include <stdio.h>
#include <math.h>
double square(double x) { return x * x; }
double cube(double x) { return x * x * x; }
double sqrt_approx(double x) { return sqrt(x); }
int main() {
double (*ops[3])(double) = {square, cube, sqrt_approx};
const char* names[] = {"bình phương", "lập phương", "căn bậc hai"};
double value = 16.0;
for (int i = 0; i < 3; ++i) {
printf("%.1f %s = %.2f\n", value, names[i], ops[i](value));
}
return 0;
}
Kết quả:
16.0 bình phương = 256.00
16.0 lập phương = 4096.00
16.0 căn bậc hai = 4.00