Bài này sẽ trình bày một số ví dụ ứng dụng con trỏ hàm:
1. Supervisor call interface
Lưu ý: Chương trình phải build x86 (32-bit)
Giải thích:
2. Mô phỏng quá trình pass sự kiện
Giải thích:
Lưu ý:
Bài trước: [C] Function Pointer (Con trỏ hàm) - P1
Bài tiếp:
1. Supervisor call interface
C:
#include <stdio.h>
#include <stdint.h>
/* Library */
int add(int a, int b) {
return a + b;
}
/* End of library */
int main()
{
int svc;
svc = (int)(&add);
int sum = ((int(*)(int,int))svc)(100, 200);
printf("sum=%d\n", sum);
return 0;
}
sum=300
Giải thích:
- Ta khai báo biến
svc
kiểuint
, do đósvc
có chiều dài 4 bytes, đủ để lưu địa chỉ của một hàm svc = (int)(&add);
Lấy địa chỉ hàmadd
, ép sang kiểuint
và lưu vào biếnsvc
- Lúc này,
svc
là một biến bình thường, không phải biến con trỏ nhưng lại lưu địa chỉ của hàm int sum = ((int(*)(int,int))svc)(100, 200);
Ép kiểusvc
từint
qua(int(*)(int,int)
và dùng nó để gọi hàmadd
- Thoạt nhìn ta thấy việc ép kiểu từ
(int(*)(int,int)
quaint
rồi lại từint
qua(int(*)(int,int)
có vẻ khá vô nghĩa, nhưng thực ra là nó vô nghĩa thật ;) - Mục đích của ví dụ trên là để chứng tỏ cho các bạn thấy rằng ta có thể gọi một hàm nếu biết địa chỉ của hàm đó. Rõ ràng
svc
là một số nguyên 4 bytes và ta có thể gán rất nhiều giá trị khác nhau cho biến này. Nhưng bằng cách nào đó, ta biết được địa chỉ của hàmadd
và gán nó vào biếnsvc
. Khi đó ta có thể gọi hàmadd
thông qua biếnsvc
.
2. Mô phỏng quá trình pass sự kiện
C:
#include <stdio.h>
#include <stdint.h>
#include <ctype.h>
/* Library */
typedef void (*on_char_input_handler_t)(char);
on_char_input_handler_t on_char_input_handler;
void set_on_char_input_handler(on_char_input_handler_t ptr) {
on_char_input_handler = ptr;
}
void run() {
while (1) {
char c = getchar();
on_char_input_handler(c);
}
}
/* End of library */
void print_char(char chr) {
printf("%c", toupper(chr));
}
int main()
{
set_on_char_input_handler(&print_char);
run();
return 0;
}
a
A
b
B
A
b
B
- Chương trình trên yêu cầu nhập vào ký tự và in ra màn hình ký tự in hoa tương ứng.
- Phần trong khung
/* Library */
và/* End of library */
là phần thư viện và có thể được viết ở một file C khác. - Trong ví dụ trên, ta đặc biệt chú ý vào hàm
set_on_char_input_handler(&print_char);
. Hàm này chỉ CPU hãy chạy hàmvoid print_char(char chr)
khi có sự kiện: có một ký tự được nhập vào. - Hàm
void print_char(char chr)
được viết trong chương trình chính và ta hoàn toàn có thể thay thế hàm này bằng các hàm khác miễn là nó có kiểuvoid (*)(char)
. Đây là nguyên lý để chỉ CPU thực hiện một công việc nào đó khi có sự kiện kiện xảy ra.
Lưu ý:
- Phần trong khung
/* Library */
và/* End of library */
là phần thư viện và có thể được viết ở một file C khác. - Mục tiêu chương trình là chạy 3 hàm với yêu cầu sau đây:
STT | Hàm | Yêu cầu |
---|---|---|
1 | void two_s_repeat_thread(char*) | Cứ 2 giây chạy một lần |
2 | void five_s_singleshot_thread(char*) | Chạy sau 5s kể từ khi bắt đầu chương trình, chỉ chạy một lần |
3 | void ten_s_singleshot_thread(char* ctx) | Chạy sau 10s kể từ khi bắt đầu. Dừng quá trình chạy lặp sau mỗi 2s của hàm 1 |
C:
#include <stdio.h>
#include <stdint.h>
#include <ctype.h>
#include <time.h>
/* Library */
#define MAX_THREAD 10
typedef void (*thread_func_t)(char*);
typedef int thread_id_t;
typedef enum {
THREAD_REPEAT, // Function run periodically
THREAD_SINGLESHOT // Function only run one time
}thread_type_t;
typedef struct {
thread_func_t thread_func; // Pointer to function
int period; // Period of function
int cnt; // Internal counter
thread_type_t type; // Type of thread
char* ctx; // Give this to function when run it
}thread_t;
thread_t m_thread[MAX_THREAD];
thread_id_t register_thread(thread_func_t thread_func,int period_ms, thread_type_t type, char* context) {
for (int i = 0; i < MAX_THREAD; i++) {
if (m_thread[i].thread_func == NULL) // Check if is there any empty slot
{
m_thread[i].thread_func = thread_func;
m_thread[i].period = period_ms;
m_thread[i].cnt = period_ms;
m_thread[i].type = type;
m_thread[i].ctx = context;
return i;
}
}
return -1; // if there is no slot remain, return invalid id
}
bool unregister_thread(thread_id_t proc_id) {
if (m_thread[proc_id].thread_func != NULL) {
m_thread[proc_id].thread_func = NULL;
return true;
}
else {
return false;
}
}
void one_ms_timer_interrupt() {
for (int i = 0; i < MAX_THREAD; i++) {
if (m_thread[i].thread_func != NULL) {
m_thread[i].cnt--; // Reduce internal counter
if (m_thread[i].cnt == 0) { // Check if timeout
m_thread[i].thread_func(m_thread[i].ctx); // Run fucntion with context
if (m_thread[i].type == THREAD_REPEAT) { // Check thread type
m_thread[i].cnt = m_thread[i].period; // Reset counter
}
else if(m_thread[i].type == THREAD_SINGLESHOT)
{
m_thread[i].thread_func = NULL; // Unregister thread
}
}
}
}
}
/* End of library */
void two_s_repeat_thread(char*) {
printf("This is 2s repeat thread\n");
}
void five_s_singleshot_thread(char*) {
printf("This is 5s repeat thread\n");
}
void ten_s_singleshot_thread(char* ctx) {
bool ok = unregister_thread((thread_id_t)*ctx);
if (ok) {
printf("Succeed to unregister thread with id %d \n", (thread_id_t)*ctx);
}
else {
printf("Failed to unregister thread with id %d \n", (thread_id_t)*ctx);
}
}
int main()
{
thread_id_t id0 = register_thread(&two_s_repeat_thread, 2000, THREAD_REPEAT, NULL);
thread_id_t id1 = register_thread(&five_s_singleshot_thread, 5000, THREAD_SINGLESHOT, NULL);
thread_id_t id2 = register_thread(&ten_s_singleshot_thread, 10000, THREAD_SINGLESHOT, (char*)&id0);
while (1) {
one_ms_timer_interrupt();
// Delay 1ms
clock_t start_time = clock();
while (clock() < start_time + 1);
}
return 0;
}
This is 2s repeat thread
This is 2s repeat thread
This is 5s repeat thread
This is 2s repeat thread
This is 2s repeat thread
This is 2s repeat thread
Succeed to unregister thread with id 0
This is 2s repeat thread
This is 5s repeat thread
This is 2s repeat thread
This is 2s repeat thread
This is 2s repeat thread
Succeed to unregister thread with id 0
Bài trước: [C] Function Pointer (Con trỏ hàm) - P1
Bài tiếp:
Last edited: