Thiết kế mã hóa C - con trỏ hàm?


24

Tôi có PIC18F46K22 và lập trình nó với trình biên dịch XC8. Cuối cùng, tôi sẽ có một hệ thống giống như một máy tính với stdinstdout. Vì vậy, trong vòng lặp chính sẽ có một chức năng đang kiểm tra nếu có đầu vào mới. Nếu có đầu vào, một hàm sẽ được gọi tương ứng. Vì vậy, ví dụ khi tôi nhập A vào stdin, PIC sẽ chạy một hàm như function_Athay vì function_Bđược gọi khi tôi nhập B.

Khi PIC được thực hiện với chức năng, tôi muốn rằng đầu vào mới được gửi đến chức năng. Vì vậy, khi nhấn A sẽ mở bộ phát RS232, từ thời điểm đó, mọi đầu vào sẽ được gửi qua RS232. Cuối cùng, dự án là một trình soạn thảo văn bản độc lập. Vì vậy, khi nhấn A sẽ mở hệ thống tệp, từ lúc đó bạn không chỉnh sửa văn bản nữa mà xem qua danh sách các tệp. Điều đó có nghĩa là nhấn Lên và Xuống có nghĩa là một cái gì đó khác với trong môi trường chỉnh sửa văn bản.

Tôi đã suy nghĩ rất nhiều về cách lập trình này trong C. Tôi đã nghĩ đến chuyện tối qua và muốn biết liệu nó có thể không và nếu có thì làm thế nào. Điều tôi muốn làm là:

  • Các mainchức năng gọi một chức năng nhưfunction_A
  • function_Athay đổi một biến toàn cục function_addrthành con trỏ địa chỉ của hàmin_function_A
  • Từ thời điểm đó, maingọi hàm function_addrkhi có đầu vào mới.

Vì vậy, những gì tôi cần là một mainchức năng kiểm tra nếu function_addrbằng không. Nếu đúng như vậy, một hàm 'bình thường' sẽ được gọi, như thế function_A. Nếu không, hàm function_addrsẽ được gọi. Tôi cũng cần một function_Acái mà thay đổi function_addrcon trỏ thành in_function_A.

Lưu ý: khi đóng chức năng hệ thống tập tin, is_function_Achỉ nên thay đổi function_addrthành 0.

Vì vậy, về cơ bản câu hỏi của tôi là làm thế nào tôi có thể

  • Lấy địa chỉ của hàm (và lưu nó trong một biến)
  • Gọi một chức năng tại một địa chỉ được chỉ định

6
Đề ra. Thuộc về stackoverflow.com.
dùng207421

Đối với tôi, một cách tiếp cận máy trạng thái ít rủi ro hơn nhiều so với xử lý các con trỏ hàm. Byte đầu vào của bạn được chuyển đến cấu trúc máy trạng thái (có thể đơn giản như trường hợp chuyển đổi) nhảy đến các bit mã khác nhau dưới dạng hàm của biến trạng thái hoặc biến. Ngoài ra, bạn không hoàn toàn rõ ràng về việc stdin của bạn đến từ đâu (không phải nó thực sự quan trọng lắm, nhưng tôi tò mò).
Adam Lawrence

9
@EJP Tôi không đồng ý. Chỉ vì có sự trùng lặp không có nghĩa là nó phải ở trên trang này hay trang kia. Nó đang đặt câu hỏi liên quan đến thiết kế và lập trình của các hệ thống nhúng cấp thấp, có vẻ như về chủ đề này.
Kortuk

Các máy trạng thái có thể dựa trên chức năng chuyển hướng chức năng: lệnh gọi hàm chuyển trạng thái trả về hàm chuyển trạng thái mới.
Kaz

1
Hãy thảo luận ở đây .

Câu trả lời:


21

Một chức năng

int f(int x){ .. }

Lấy địa chỉ của hàm (và lưu nó trong một biến)

int (*fp)(int) = f;

Gọi một chức năng tại một địa chỉ được chỉ định

int x = (*fp)( 12 );

3
+1, rõ ràng trong thế giới C và ASM, nhưng không quá rõ ràng đối với những người bắt đầu với ngôn ngữ OO.
Anindo Ghosh

4
Cuộc gọi fp cũng có thể được viết là 'int x = fp (12);' để tăng khả năng đọc.
Rev1.0

1
Tôi phải thừa nhận, hiện đang làm việc chủ yếu trong C # và Java, đôi khi tôi nhớ những con trỏ hàm cũ tốt từ C này. Chúng nguy hiểm khi không được thực hiện chính xác và hầu hết các nhiệm vụ có thể được thực hiện theo những cách khác, nhưng chúng chắc chắn rất hữu ích trong vài lần khi chúng thực sự có ích.
một CVn

@ MichaelKjorling: Thật vậy. Tôi liên tục chuyển đổi giữa lập trình C và C # nhúng (thậm chí triển khai mã tương tự để liên lạc giữa thiết bị và PC) và mạnh mẽ như C #, tôi vẫn thích C rất nhiều.
Rev1.0

2
fp(12)không tăng khả năng đọc hơn (*fp)(12). Họ có khả năng đọc khác nhau. (*fp)(12)nhắc nhở người đọc đó fplà một con trỏ, và cũng giống như khai báo fp. Trong tâm trí của tôi, phong cách này được liên kết với các nguồn cũ, như nội bộ X11 và K & R C. Một lý do có thể có liên quan đến K & R C là vì trong ANSI C, có thể khai báo fpbằng cú pháp hàm, nếu fplà một tham số: int func(int fp(double)) {}. Trong K & R C đó sẽ có được int func(fp) int (*fp)(double); { ... }.
Kaz

17

Trong khi câu trả lời của Wouters hoàn toàn chính xác, điều này có thể thân thiện với người mới bắt đầu hơn và một ví dụ tốt hơn về câu hỏi của bạn.

int (*fp)(int) = NULL;

int FunctionA(int x){ 
    return x + x;
}

int FunctionB(int x){ 
    return x * x;
}

void Example(void) {
    int x = 0;

    fp = FunctionA;
    x = fp(3);  /* after this, x == 6 */

    fp = FunctionB;
    x = fp(3);  /* after this, x == 9 */
}

Nếu không rõ ràng rằng con trỏ hàm thực sự có một địa chỉ hàm mục tiêu được gán, thì nên thực hiện kiểm tra NULL.

if (fp != NULL)
    fp(); /* with parameters, if applicable */

8
+1 cho kiểm tra null, nhưng lưu ý rằng trình biên dịch có thể không đảm bảo rằng giá trị tại địa chỉ trong RAM fpxảy ra thực sự là NULL ban đầu. Tôi chắc chắn sẽ làm int (*fp)(int) = NULL;như một tuyên bố và khởi tạo kết hợp để chắc chắn - bạn không muốn chuyển đến một vị trí bộ nhớ ngẫu nhiên vì một số giá trị ngớ ngẩn vừa xảy ra ở đó. Sau đó, chỉ cần gán fpnhư trong Example()chức năng của bạn .
một CVn

1
Cảm ơn các bình luận, tôi đã thêm khởi tạo NULL.
Rev1.0

4
@ MichaelKjorling điểm tốt, nhưng nếu fplà "biến toàn cục" (như mã anh ta ở trên), thì nó được đảm bảo để được khởi tạo NULL(mà không cần phải làm rõ ràng như vậy).
Alok
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.