Buraya Nasıl Geldik
İşlev noktalarını bildirmek için kullanılan C sözdiziminin kullanımı yansıtması amaçlanmıştır. Aşağıdaki gibi düzenli bir işlev bildirimi düşünün <math.h>
:
double round(double number);
Bir nokta değişkenine sahip olmak için bunu tip güvenliği ile atayabilirsiniz.
fp = round;
bu fp
nokta değişkenini şu şekilde beyan etmeniz gerekir :
double (*fp)(double number);
Yapmanız gereken tek şey Yani işlevini kullanın ve yapım bir işaretçi referansı ile bu işlevin adını yerini alacak nasıl bakmak round
içine *fp
. Bununla birlikte, bazılarının biraz daha karışık hale getirdiğini söyleyecek ekstra bir parens setine ihtiyacınız var.
Muhtemelen, bu işlev imzası bile olmayan orijinal C'de daha kolaydı, ama oraya geri dönmeyelim, tamam mı?
Özellikle kötü hale geldiği yer, bir argüman olarak alan veya bir işleve bir işaretçi döndüren bir işlevi veya her ikisini birden nasıl bildireceğini bulmaktır.
Bir fonksiyonunuz varsa:
void myhandler(int signo);
bunu sinyal fonksiyonuna (3) şu şekilde aktarabilirsiniz:
signal(SIGHUP, myhandler);
ya da eski işleyiciyi saklamak istiyorsanız,
old_handler = signal(SIGHUP, new_handler);
ki bu oldukça kolay. Oldukça kolay - ne güzel, ne de kolay - beyanları doğru yapmaktır.
signal(int signo, ???)
Eh, işlev bildiriminize geri dönün ve bir nokta referansı için adı değiştirin:
signal(int sendsig, void (*hisfunc)(int gotsig));
Beyan etmediğiniz için gotsig
, atlarsanız okumayı daha kolay bulabilirsiniz:
signal(int sendsig, void (*hisfunc)(int));
Ya da olmayabilir. :(
Bunun yeterince iyi olmaması dışında, sinyal (3) de eski işleyiciyi aşağıdaki gibi döndürür:
old_handler = signal(SIGHUP, new_handler);
Şimdi tüm bunları nasıl ilan edeceğinizi anlamalısınız.
void (*old_handler)(int gotsig);
atayacağınız değişken için yeterlidir. gotsig
Sadece burada gerçekten beyan etmedığınızı unutmayın old_handler
. Bu gerçekten yeterli:
void (*old_handler)(int);
Bu bizi sinyal (3) için doğru bir tanımlamaya getirir:
void (*signal(int signo, void (*handler)(int)))(int);
Kurtarmaya Tip Tanımlar
Bu zamana kadar herkesin bunun bir karmaşa olduğunu kabul edeceğini düşünüyorum. Bazen soyutlamalarınızı adlandırmak daha iyidir; sık sık, gerçekten. Doğru ile typedef
, bunu anlamak çok daha kolay hale gelir:
typedef void (*sig_t) (int);
Artık kendi işleyici değişkeniniz
sig_t old_handler, new_handler;
ve sinyal (3) için beyanınız
sig_t signal(int signo, sig_t handler);
aniden anlaşılır. * 'Dan kurtulmak da kafa karıştırıcı parantezlerden bazılarını ortadan kaldırır (ve ebeveynlerin her zaman anlaşılmasını kolaylaştırdığını söylerler - hah!). Kullanımınız hala aynı:
old_handler = signal(SIGHUP, new_handler);
ama şimdi için bildirimleri anlama şansına sahip old_handler
, new_handler
ve hattasignal
onları ilk veya ihtiyaç karşılaştıklarında onları yazmak için.
Sonuç
ÇokGörünüşe göre, az sayıda C programcısı, referans materyallere danışmadan bu şeyler için doğru beyanları kendileri tasarlayabiliyor.
Biliyorum, çünkü bir zamanlar çekirdek ve aygıt sürücüsü çalışması yapan kişiler için röportaj sorularımızda bu soruyu sorduk. :) Tabii, onlar beyaz tahta çöktü ve yandı gibi çok sayıda aday kaybettik. Ancak, bu alanda daha önce deneyime sahip olduklarını iddia eden, ancak işi yapamayacağını iddia eden kişileri işe almaktan kaçındık.
Bu yaygın zorluk nedeniyle, muhtemelen sadece mantıklı değil, aynı zamanda sadece bunu kullanmak için ortalamanın üzerinde üç sigma oturan bir üçlü alfa geek programcısı olmanızı gerektirmeyen tüm bu beyanları gözden geçirmenin bir yoluna sahip olmak muhtemelen mantıklıdır. bir şey rahatça.
f :: (Int -> Int -> Int) -> Int -> Int