- vừa được xem lúc

Con Trỏ Hàm - C/C++

0 0 12

Người đăng: Hà Thanh Bình

Theo Viblo Asia

Hàm Con Trỏ (Function Pointer) là gì?

  • Hàm con trỏ trong lập trình đề cập đến:
    • Con trỏ tới hàm (A pointer to a function): Đây là biến lưu trữ địa chỉ hàm mà sau này có thể gọi bằng con trỏ này.
    • Hàm trả về một con trỏ (Function returning Pointers): Đây là hàm trả về địa chỉ bộ nhớ trỏ đến một biến haowjc cấu trấu dữ liệu

A pointer to function

Định nghãi và cách sử dụng

Con trỏ tới một hàm cho phép bạn tự động quyết định hàm nào sẽ được gọi trong thời gian chạy, thay vì thời gian biên dịch. Điều này mang lại sự linh hoạt và có thể được sử dụng để triển khai các lệnh gọi lại (callbacks), trình xử lý sự kiện (event handlers), và để triển khai các cấu trúc mã trừu tượng hơn như máy trạng thái (State Machine)


#include <stdio.h> // khai báo một hàm đơn giản để được trỏ tới
void sayHello(){ printf("Hello World");
} // một hàm khác cũng sẽ được trỏ tới
void sayGoodbye(){ printf("Goodbye, World!\n");
} int main(){ // khai báo một con trỏ hàm void void (*funcPtr)(); // trỏ tới hàm sayHello funcPtr = sayHello; // gọi hàm thông qua con trỏ funcPtr(); // -> in ra: Hello World // trỏ tới hàm sayGoodbye funcPtr = sayGoodbye; funcPtr(); // -> in ra: Goodbye, World! return 0; 
Lý do sử dụng Hàm con trỏ (Function pointer)
  • Dynamic Function Call: Chúng cho phép gọi hàm động, nghĩa là bạn có thể chòn hàm nào sẽ thực thi khi chạy
  • Callbacks: được sử dụng trong lệnh gọi lại cho lập trình hướng sự kiện
  • Flexibility (Tính linh hoạt): Cho phép truyền các hàm làm đối số cho các hàm khác, tạo điều kiện thuận lợi cho việc tạo mã linh hoạt hơn và có thể tái sử dụng
  • Efficiency (Hiệu quả): Có thể làm cho việc triển khai state machine hiệu quả hơn bằng cách giảm như cầu về nhiều cấu trúc if-else hoặc switch-case.

Function Returning Pointers

Định nghĩa và cách sử dụng

Các hàm này trả về một con trỏ, thường được sử dụng để trả về địa chỉ của một mảng hoặc bộ nhớ được cấp phát động (Dynamically allocated memory)

#include <stdio.h>
#include <stdlib.h> // hàm trả về một con trỏ
int* allocateArray(int Size){ int* array = (int*)malloc(size * sizeof(int)); return array;
} int main(){ int size = 5; int* array = allocatedArray(size); // khởi tạo mảng for (int i = 0; i < size; i++){ printf("%d", array[i]); } // giải phóng bộ nhớ động free(array); return 0;

Ứng dụng trong hệ thống nhúng (Embedded System)

Trong các hệ thống nhúng, nơi tài nguyên bị hạn chế và hiệu suất thời gian thực là quan trọng, pointers được sử dụng rộng rãi vì nhiều lý do:


1. Xử lý ngắt (Interrupt Handling):

  • Con trỏ hàm được sử dụng để thiết lập các chương trình phục vụ ngắt (Interrupt Service Routine - ISR). Địa chỉ của ISR được lưu trữ trong bảng vector ngắt (Interrupt vetor table).
#include <stdio.h> // ISRs prototypes
void UART_ISR();
void Timer_ISR(); // ISR vector table
void (*ISR_VectorTable[2])() = {UART_ISR, Timer_ISR}; void UART_ISR(){ printf("UART Interrupt\n");
} void Timer_ISR(){ printf("Timer Interrupt\n");
} void SimulateInterrupt(int InterruptType){ if (InterruptType < 2){ ISR_VectorTable[InterruptType](); }
} int main(){ // mô phỏng UART interrupt SimulateInterrupt(0); // mô phỏng Timer interrupt SimulateInterrupt(1); return 0;
}

2. State Machines:

  • Con trỏ hàm cho phép triển khai các máy trạng thái (state machine) bằng cách cho phép chuyển đổi động giữa các trạng thái, làm cho mã trở nên đồng nhất và dễ duy trì.
#include <stdio.h> // Nguyên mẫu hàm trạng thái
void stateA();
void stateB();
void stateC(); // định nghĩa cho một hàm trạng thái
typedef void(*StateFunc)(); // Bảng chuyển trạng thái
StateFunc stateTable[3] = {stateA, stateB, stateC}; // biến trạng thái hiện tại
int currentState = 0; void stateA(){ printf("State A\n"); // đổi sang trạng thái B currentState = 1;
} void stateB(){ printf("State B\n"); // đổi sang trạng thái C currentState = 2;
} void stateC(){ printf("State C\n"); // đổi sang trạng thái A currentState = 0;
} int main(){ // mô phonge quá trình chuyển đổi trạng thái for (int i = 0; i < 6; ++i){ stateTable[currentState](); } return 0;

3. Callbacks:

  • Được sử dụng trong bộ hẹn giờ, giao tiếp UART, I2C, SPI để xử lý các sự kiện như dữ liệu đã nhận, hoàn tất truyền, v.v
#include <stdio.h> // Callback type definition
typedef void (*CallbackFunc)(int); // Function that accepts a callback
void registerCallback(CallbackFunc cb, int eventData) { // Invoke the callback with event data cb(eventData);
} // Sample callback function
void eventHandler(int data) { printf("Event handled with data: %d\n", data);
} int main() { // Register the callback registerCallback(eventHandler, 42); return 0;
} 

4. Trừu tượng hóa trình điều khiển (Driver Abstraction):

  • Các hoạt động phần cứng sử dụng con trỏ hàm, giúp chuyển đổi phần cứng dễ dàng hơn mà không cần thay đổi mã ứng dụng cấp cao hơn.

5. Quản lý bộ nhớ động (Dynamic Memory Management):

  • Các hàm trả về con trỏ có thể được sử dụng để phân bổ và quản lý bộ nhớ động, điều này rất quan trọng trong mời trường có bộ nhớ hạn chế.

Summary

Sử dụng con trỏ hàm một cách hiệu quả trong các hệ thống nhúng liên quan đến việc hiểu lợi ích của chigns và áp dụng các phương pháp hay nhất để tối đa hóa hiệu quả, khả năng bảo trì và tính linh hoạt của mã.

Lưu ý

  1. Sự rõ ràng

    • Ghi lại rõ ràng mục đích của con trỏ hàm và cách sử dụng dự kiến của chúng
    • Đặt tên con trỏ hàm một cách mô tả để chỉ ra vai trò của chúng rõ ràng
  2. Khởi tạo (Initialization)

    • Luôn khởi tạo các con trỏ hàm trước khi sử dụng để ngăn chặn lỗi Null Pointer Dereferences
    • Xem xét các trình xử lý mặc định cho các con trỏ chưa được khởi tạo
  3. Modular Code

    • Sử dụng con trỏ hàm để tạo mã mô-đun và có thể tái sử dụng bằng cách tách các thành phần.
  4. Hiệu suất

    • Xin lưu ý rằng các lệnh gọi hàm gián tiếp thông qua con trỏ có thể chậm hơn do tình trạng ngừng hoạt động trong các CPU hiện đại. Giảm thiểu việc sử dụng chúng trong các phần quan trọng về hiệu suất.
  5. Quản lý bộ nhớ

    • Khi các hàm trả về con trỏ tới bộ nhớ được cấp phát động, hãy đảm bảo quản lý bộ nhớ thích hợp (phân bổ và giải phóng).

Bình luận

Bài viết tương tự

- vừa được xem lúc

Memory layout của một chương trình C/C++

1. Đặt Vấn Đề.

0 0 45

- vừa được xem lúc

Sự khác nhau giữa bộ nhớ Heap và bộ nhớ Stack trong lập trình

1. Giới thiệu. . Như chúng ta đã biết thì việc Quản lý bộ nhớ đối với một lập trình viên là rất quan trọng.

0 0 57

- vừa được xem lúc

Tích hợp thư viện C/C++ vào một dự án Flutter như thế nào?

Chào các bạn hôm nay rảnh rỗi thì mình viết một bài hướng dẫn về cách tích hợp một thư viện C/C++ vào một dự án Flutter. Thì cũng khá đơn giãn thôi vì Flutter đã cũng cấp cho chúng ta một thư viện khá

0 0 62

- vừa được xem lúc

Programming

Definition of Programming. .

0 0 38

- vừa được xem lúc

Gọi hàm của C++ trong Python bằng ctypes

Nếu đã từng dùng qua các thư viện liên quan đến toán học của Python như numpy, các bạn sẽ để ý thấy các nó có tốc độ xử lý rất nhanh. Điều này là do một phần của package này được viết bằng C/C++ và ph

0 0 42

- vừa được xem lúc

Build ứng dụng Cross Platform với Go và CGO

1. Giới thiệu.

0 0 37