Làm thế nào chúng ta có ở đây
Cú pháp C để khai báo các điểm chức năng được dự định để phản ánh việc sử dụng. Hãy xem xét một khai báo hàm thông thường như thế này từ <math.h>
:
double round(double number);
Để có một biến điểm, bạn có thể gán nó với loại an toàn bằng cách sử dụng
fp = round;
bạn cần phải khai báo fp
biến điểm đó theo cách này:
double (*fp)(double number);
Vì vậy, tất cả những gì bạn phải làm là xem cách bạn sẽ sử dụng hàm và thay thế tên của hàm đó bằng một tham chiếu con trỏ, tạo round
thành *fp
. Tuy nhiên, bạn cần thêm một bộ parens, mà một số người sẽ nói làm cho nó hơi lộn xộn hơn.
Có thể cho rằng, điều này từng dễ dàng hơn trong C ban đầu, thậm chí không có chữ ký hàm, nhưng chúng ta đừng quay lại đó, được chứ?
Nơi nó trở nên đặc biệt khó chịu là tìm ra cách khai báo một hàm lấy làm đối số hoặc trả về một con trỏ cho hàm hoặc cả hai.
Nếu bạn có một chức năng:
void myhandler(int signo);
bạn có thể chuyển nó đến chức năng tín hiệu (3) theo cách này:
signal(SIGHUP, myhandler);
hoặc nếu bạn muốn giữ trình xử lý cũ, thì
old_handler = signal(SIGHUP, new_handler);
đó là khá dễ dàng. Điều khá dễ dàng - không đẹp, cũng không dễ - là nhận được các tuyên bố đúng.
signal(int signo, ???)
Chà, bạn chỉ cần quay lại khai báo hàm và hoán đổi tên để tham chiếu điểm:
signal(int sendsig, void (*hisfunc)(int gotsig));
Vì bạn không khai báo gotsig
, bạn có thể thấy dễ đọc hơn nếu bạn bỏ qua:
signal(int sendsig, void (*hisfunc)(int));
Hoặc có thể không. :
Ngoại trừ điều đó là không đủ, bởi vì tín hiệu (3) cũng trả về trình xử lý cũ, như trong:
old_handler = signal(SIGHUP, new_handler);
Vì vậy, bây giờ bạn phải tìm ra cách khai báo tất cả những cái đó.
void (*old_handler)(int gotsig);
là đủ cho biến bạn sẽ gán cho. Lưu ý rằng bạn không thực sự tuyên bố gotsig
ở đây, chỉ old_handler
. Vì vậy, điều này thực sự là đủ:
void (*old_handler)(int);
Điều đó đưa chúng ta đến một định nghĩa chính xác cho tín hiệu (3):
void (*signal(int signo, void (*handler)(int)))(int);
Typedefs để giải cứu
Đến lúc này, tôi nghĩ mọi người sẽ đồng ý rằng đó là một mớ hỗn độn. Đôi khi, tốt hơn là đặt tên cho sự trừu tượng của bạn; thường xuyên Với quyền typedef
, điều này trở nên dễ hiểu hơn nhiều:
typedef void (*sig_t) (int);
Bây giờ biến xử lý của riêng bạn trở thành
sig_t old_handler, new_handler;
và tuyên bố của bạn cho tín hiệu (3) trở thành
sig_t signal(int signo, sig_t handler);
đó là bất ngờ dễ hiểu. Loại bỏ các dấu * cũng loại bỏ một số dấu ngoặc đơn khó hiểu (và họ nói rằng các phép ẩn luôn làm cho mọi thứ dễ hiểu hơn - hah!). Cách sử dụng của bạn vẫn giống nhau:
old_handler = signal(SIGHUP, new_handler);
nhưng bây giờ bạn có cơ hội tìm hiểu các tờ khai old_handler
, new_handler
và thậm chí signal
khi bạn lần đầu tiên gặp họ hay cần phải viết chúng.
Phần kết luận
Rất ít lập trình viên C, hóa ra, có khả năng tự mình đưa ra các tuyên bố chính xác cho những điều này mà không cần tham khảo tài liệu tham khảo.
Tôi biết, bởi vì chúng tôi đã từng có câu hỏi này trong các câu hỏi phỏng vấn của chúng tôi cho những người làm công việc điều khiển nhân và thiết bị. :) Chắc chắn, chúng tôi đã mất rất nhiều ứng cử viên theo cách đó khi họ bị rơi và bị đốt cháy trên bảng trắng. Nhưng chúng tôi cũng tránh thuê những người tuyên bố rằng họ có kinh nghiệm trước đây trong lĩnh vực này nhưng thực sự không thể làm được việc.
Tuy nhiên, do khó khăn phổ biến này, có lẽ không chỉ hợp lý mà thực sự hợp lý để có một cách để giải quyết tất cả những tuyên bố đó không còn yêu cầu bạn phải là một lập trình viên ba-geek ngồi trên ba sigmas chỉ để sử dụng điều này loại điều thoải mái.
f :: (Int -> Int -> Int) -> Int -> Int