วงเล็บรอบชื่อฟังก์ชันหมายถึงอะไร


214

ในไฟล์ต้นฉบับโครงการหนึ่งฉันพบนิยามฟังก์ชัน C นี้:

int (foo) (int *bar)
{
    return foo (bar);
}

หมายเหตุ: ไม่มีเครื่องหมายดอกจันติดกับfooดังนั้นจึงไม่ใช่ตัวชี้ฟังก์ชัน หรือมันคืออะไร? เกิดอะไรขึ้นกับการโทรซ้ำ?


7
ไม่มันไม่ใช่ตัวชี้ฟังก์ชั่น - มันยังคงเป็นฟังก์ชั่นปกติที่ชื่อ foo
Nemanja Boric

นี่เป็นฟังก์ชั่นที่สมบูรณ์หรือไม่
asheeshr

2
คุณมีหลักฐานว่าฟังก์ชันนี้ใช้ในบริบทที่มีประโยชน์หรือไม่
moooeeeep

1
... ดูเหมือนว่าฟังก์ชั่นหลอกตาที่อาจจะเขียนเพื่อดูว่ามันรวบรวมในแหล่งที่มีอยู่และควรจะถูกลบออกหรือไม่ ฉันจะลบมัน (ถ้านั่นคือสิ่งที่ฟังก์ชั่นใช้งานได้จริง) เนื่องจากอย่างดีที่สุดมันจะวนซ้ำไม่สิ้นสุด (ฉันไม่แน่ใจว่า C คอมไพเลอร์ได้รับอนุญาตให้ปรับการเรียกหางให้เหมาะสมที่สุดหรือไม่)
hyde

3
เครื่องหมายวงเล็บในการประกาศ C ช่วยในการคลี่คลายภาษาที่ไม่ชัดเจน ด่วนคือa(b);อะไร ประกาศbว่าเป็นตัวแปรชนิดaหรือไม่? หรือเรียกร้องให้ฟังก์ชั่นที่aมีข้อโต้แย้งb? ความแตกต่างคือวากยสัมพันธ์และคุณไม่สามารถรู้วิธีที่จะแยกมันได้โดยไม่ต้องค้นหาข้อมูลการประกาศของa; นั่นคือการเรียกใช้ฟังก์ชั่น postfix วงเล็บหรือวงเล็บไม่จำเป็นรอบประกาศ
Kaz

คำตอบ:


329

ในกรณีที่ไม่มีตัวประมวลผลล่วงหน้าเกิดขึ้นfooลายเซ็นของเทียบเท่ากับ

int foo (int *bar)

บริบทเดียวที่ฉันเห็นผู้คนใส่วงเล็บที่ไม่จำเป็นซึ่งดูเหมือนไม่จำเป็นไว้รอบชื่อฟังก์ชั่นคือเมื่อมีทั้งฟังก์ชั่นและมาโครคล้ายฟังก์ชันที่มีชื่อเหมือนกันและโปรแกรมเมอร์ต้องการป้องกันการขยายตัวของแมโคร

วิธีนี้อาจดูแปลก ๆ ในตอนแรก แต่ห้องสมุด C ชุดแบบอย่างโดยให้แมโครและฟังก์ชั่นที่มีชื่อเหมือน

หนึ่งฟังก์ชั่นดังกล่าว / isdigit()คู่แมโคร ไลบรารีอาจกำหนดดังต่อไปนี้:

/* the macro */
#define isdigit(c) ...

/* the function */
int (isdigit)(int c) /* avoid the macro through the use of parentheses */
{
  return isdigit(c); /* use the macro */
}

ฟังก์ชั่นของคุณมีลักษณะเกือบเหมือนด้านบนดังนั้นฉันสงสัยว่านี่คือสิ่งที่เกิดขึ้นในโค้ดของคุณเช่นกัน


2
อาจเป็นกรณีที่นี่เช่นกัน; ฉันไม่ได้มองหามาโคร ... และฉันไม่รู้ว่าการขยายมาโครไม่ได้เกิดขึ้นในวงเล็บขอบคุณที่ชี้ให้เห็น!
user1859094

13
@ user1859094: ในลักษณะที่สองนี่เป็นสิ่งที่เกิดขึ้นในรหัสของคุณ foo(bar)ภายในฟังก์ชั่นที่มีการใช้แมโครที่สอดคล้องกัน
NPE

78
@ user1859094 การขยายตัวของแมโคจะเกิดขึ้นภายในวงเล็บ แต่การขยายตัวของมาโครที่มีฟังก์ชั่นคล้ายเกิดขึ้นก็ต่อเมื่อโทเค็นถัดไปเป็นวงเล็บซ้าย (C99, 6.10.3§10) ดังนั้นfoo (int* bar)จะถูกแทนที่ แต่ไม่ใช่(foo) (int *bar)(โทเค็นถัดไป หลังจากfooนั้น))
Virgile

4
ฟังก์ชั่นดังกล่าวจะถูกเรียกว่าอย่างไร? คุณจะเรียกมันว่าวงเล็บด้วยหรือไม่ เช่นจะนี้ทำงาน: (isdigit)(5)?
gcochard

4
@Greg: ใช่นั่นคือวิธีที่คุณจะเรียกมันว่า
NPE

37

วงเล็บไม่เปลี่ยนประกาศ - fooมันยังคงเป็นเพียงการกำหนดฟังก์ชั่นสามัญที่เรียกว่า

เหตุผลที่พวกเขาถูกนำมาใช้นั้นเกือบจะแน่นอนเพราะมีมาโครคล้ายฟังก์ชันที่เรียกว่าfooกำหนด:

#define foo(x) ...

การใช้(foo)ในการประกาศฟังก์ชันป้องกันไม่ให้แมโครนี้ขยายได้ที่นี่ ดังนั้นสิ่งที่มีแนวโน้มที่จะเกิดขึ้นคือฟังก์ชั่นจะถูกกำหนดด้วยร่างของมันถูกขยายจากฟังก์ชั่นเหมือนแมโครfoo()foo


5
การหักที่ดี (แม้ว่าการใช้วงเล็บเพื่อจุดประสงค์นี้ควรมีโทษตามกฎหมาย)
ugoren

3
@ugoren: การใช้ parens รอบชื่อฟังก์ชั่นเป็นเพียงวิธีเดียวในการป้องกันการขยายตัวของแมโครสำหรับมาโครคล้ายฟังก์ชัน บางครั้งมันเป็นเครื่องมือที่จำเป็น
Michael Burr

7
@MichaelBurr นอกจากนี้ยังมีตัวเลือกในการไม่มีมาโครและฟังก์ชันที่ใช้ชื่อเดียวกัน ฉันรู้ว่าคุณไม่สามารถควบคุมทุกอย่างได้เสมอ แต่ถ้าคุณมาถึงวิธีนี้ฉันจะบอกว่ามีบางอย่างผิดปกติมาก
ugoren

-3

วงเล็บไม่มีความหมาย
รหัสที่คุณแสดงนั้นเป็นเพียงการเรียกซ้ำแบบไม่สิ้นสุด

เมื่อกำหนดตัวชี้ฟังก์ชั่นบางครั้งคุณจะเห็นวงเล็บแปลก ๆ ที่มีความหมายอะไรบางอย่าง แต่นี่ไม่ใช่กรณีที่นี่


6
ไม่ชัด วงเล็บป้องกันการขยายตัวของแมโคร ดูคำตอบที่ยอมรับได้
Kevin

12
@ เควินคำตอบของฉันเกี่ยวกับรหัสที่แสดงและถูกต้องสำหรับมัน ในเกือบทุกคำถาม C ที่นี่สมมติว่าคำจำกัดความของตัวประมวลผลล่วงหน้าที่ไม่รู้จักสามารถเปลี่ยนแปลงทุกอย่างได้ ในกรณีนี้คำตอบที่พิจารณาว่าตัวประมวลผลล่วงหน้าดีกว่าจริง ๆ แต่ก็ไม่ได้ทำให้ฉันผิด
ugoren
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.