เรามาถึงที่นี่ได้อย่างไร
ไวยากรณ์ C สำหรับการประกาศคะแนนฟังก์ชั่นมีวัตถุประสงค์เพื่อสะท้อนการใช้งาน พิจารณาการประกาศฟังก์ชั่นปกติเช่นนี้จาก<math.h>
:
double round(double number);
หากต้องการตัวแปรจุดคุณสามารถกำหนดให้กับประเภทความปลอดภัยโดยใช้
fp = round;
คุณจะต้องประกาศfp
ตัวแปรนั้นด้วยวิธีนี้:
double (*fp)(double number);
ดังนั้นสิ่งที่คุณต้องทำคือการดูที่วิธีการที่คุณจะใช้ฟังก์ชั่นและเปลี่ยนชื่อของฟังก์ชั่นที่มีการอ้างอิงตัวชี้ทำให้เข้าround
*fp
อย่างไรก็ตามคุณจำเป็นต้องมีชุดของ parens พิเศษซึ่งบางคนอาจบอกว่าทำให้มันยุ่งยากขึ้น
เนื้อหานี้เคยง่ายกว่าเดิมใน C ดั้งเดิมซึ่งไม่ได้มีลายเซ็นของฟังก์ชั่น แต่ก็อย่ากลับไปที่นั่นใช่ไหม
สถานที่ที่น่ารังเกียจโดยเฉพาะอย่างยิ่งคือการหาวิธีการประกาศฟังก์ชั่นที่ใช้เป็นอาร์กิวเมนต์หรือส่งกลับตัวชี้ไปยังฟังก์ชั่นหรือทั้งสองอย่าง
หากคุณมีฟังก์ชั่น:
void myhandler(int signo);
คุณสามารถส่งผ่านไปยังฟังก์ชั่นสัญญาณ (3) ด้วยวิธีนี้:
signal(SIGHUP, myhandler);
หรือถ้าคุณต้องการเก็บ handler เก่าไว้
old_handler = signal(SIGHUP, new_handler);
ซึ่งค่อนข้างง่าย อะไรคือสิ่งที่เรียบง่าย - ไม่สวยและไม่ง่าย - การประกาศให้ถูกต้อง
signal(int signo, ???)
คุณเพิ่งกลับไปที่การประกาศฟังก์ชั่นและสลับชื่อสำหรับการอ้างอิงจุด:
signal(int sendsig, void (*hisfunc)(int gotsig));
เนื่องจากคุณไม่ได้ประกาศgotsig
คุณอาจพบว่าอ่านง่ายกว่าถ้าคุณละเว้น:
signal(int sendsig, void (*hisfunc)(int));
หรืออาจจะไม่ :(
ยกเว้นว่าไม่ดีพอเนื่องจากสัญญาณ (3) จะส่งคืนตัวจัดการเก่าเช่นเดียวกับใน:
old_handler = signal(SIGHUP, new_handler);
ดังนั้นคุณต้องหาวิธีที่จะประกาศสิ่งเหล่านั้นทั้งหมด
void (*old_handler)(int gotsig);
ก็เพียงพอแล้วสำหรับตัวแปรที่คุณจะกำหนดให้ โปรดทราบว่าคุณไม่ได้จริงๆประกาศที่นี่เท่านั้นgotsig
old_handler
ดังนั้นนี่ก็เพียงพอแล้ว:
void (*old_handler)(int);
นั่นทำให้เรามีคำจำกัดความที่ถูกต้องสำหรับสัญญาณ (3):
void (*signal(int signo, void (*handler)(int)))(int);
พิมพ์เพื่อช่วยเหลือ
มาถึงตอนนี้ฉันคิดว่าทุกคนจะเห็นด้วยว่านั่นเป็นเรื่องยุ่ง บางครั้งการตั้งชื่อนามธรรมของคุณดีกว่า บ่อยครั้งจริงๆ ด้วยสิทธิtypedef
สิ่งนี้จะเข้าใจได้ง่ายขึ้นมาก:
typedef void (*sig_t) (int);
ตอนนี้ตัวแปรตัวจัดการของคุณจะกลายเป็น
sig_t old_handler, new_handler;
และการประกาศของคุณสำหรับสัญญาณ (3) กลายเป็นเพียงแค่
sig_t signal(int signo, sig_t handler);
ซึ่งเข้าใจได้โดยฉับพลัน การกำจัด * ยังกำจัดวงเล็บที่ทำให้สับสน (และพวกเขาบอกว่าการล้อเลียนทำให้เข้าใจง่ายขึ้น - ฮ่า!) การใช้งานของคุณยังเหมือนเดิม:
old_handler = signal(SIGHUP, new_handler);
แต่ตอนนี้คุณมีโอกาสในการทำความเข้าใจเกี่ยวกับการประกาศสำหรับold_handler
, new_handler
และแม้กระทั่งsignal
เมื่อคุณพบพวกเขาหรือจำเป็นต้องเขียนให้พวกเขา
ข้อสรุป
ปรากฎว่าโปรแกรมเมอร์ซีน้อยมากมีความสามารถในการคิดค้นคำประกาศที่ถูกต้องสำหรับสิ่งเหล่านี้ด้วยตัวเองโดยไม่ต้องปรึกษาเอกสารอ้างอิง
ฉันรู้เพราะเราเคยมีคำถามนี้มากในคำถามสัมภาษณ์ของเราสำหรับคนที่ทำงานกับเคอร์เนลและไดรเวอร์อุปกรณ์ :) แน่นอนว่าเราแพ้ผู้สมัครจำนวนมากด้วยวิธีที่พวกเขาชนและเผาบนไวท์บอร์ด แต่เราก็หลีกเลี่ยงการจ้างคนที่อ้างว่าพวกเขาเคยมีประสบการณ์ในด้านนี้มาก่อน แต่ไม่สามารถทำงานได้จริง
เนื่องจากความยากลำบากที่แพร่หลายนี้มันอาจจะไม่ใช่แค่ความสมเหตุสมผล แต่ก็สมเหตุสมผลที่จะมีวิธีการเกี่ยวกับการประกาศทั้งหมดที่ไม่ต้องการให้คุณเป็นโปรแกรมเมอร์อัลฟ่าอัลฟ่าสามตัวนั่งซิกซิกมาเหนือค่าเฉลี่ยเพียงแค่ใช้สิ่งนี้ เรียงลำดับของสิ่งที่สะดวกสบาย
f :: (Int -> Int -> Int) -> Int -> Int