ทำความเข้าใจกับ typedefs สำหรับพอยน์เตอร์ของฟังก์ชันใน C


237

ฉันมักจะนิ่งงันอยู่เสมอเมื่อฉันอ่านรหัสของคนอื่นซึ่งมี typedefs สำหรับตัวชี้ไปยังฟังก์ชันที่มีอาร์กิวเมนต์ ฉันจำได้ว่ามันต้องใช้เวลาสักพักกว่าจะได้คำนิยามดังกล่าวในขณะที่พยายามทำความเข้าใจกับอัลกอริธึมเชิงตัวเลขที่เขียนด้วย C เมื่อไม่นานมานี้ ดังนั้นคุณสามารถแบ่งปันเคล็ดลับและความคิดของคุณเกี่ยวกับวิธีเขียน typedefs ที่ดีสำหรับตัวชี้ไปยังฟังก์ชัน (Do's and Do's Do) ทำไมพวกเขาถึงมีประโยชน์และทำความเข้าใจงานของผู้อื่นได้อย่างไร ขอบคุณ!


1
คุณสามารถยกตัวอย่างบางส่วนได้หรือไม่?
Artelius

2
คุณไม่ได้หมายถึง typedefs สำหรับพอยน์เตอร์ของฟังก์ชั่นแทนที่จะเป็นมาโครสำหรับพอยน์เตอร์ของฟังก์ชั่น? ฉันเคยเห็นอดีต แต่ไม่ใช่รุ่นหลัง
dave4420

ยังเห็นวิธีการประกาศตัวชี้ฟังก์ชัน __stdcall
jww

คำตอบ:


297

พิจารณาsignal()ฟังก์ชั่นจากมาตรฐาน C:

extern void (*signal(int, void(*)(int)))(int);

ชัดเจนอย่างสมบูรณ์แบบ - เป็นฟังก์ชั่นที่รับสองอาร์กิวเมนต์จำนวนเต็มและตัวชี้ไปยังฟังก์ชันที่ใช้จำนวนเต็มเป็นอาร์กิวเมนต์และส่งคืนค่าใด ๆ และมันsignal()ส่งคืนตัวชี้ไปยังฟังก์ชันที่ใช้จำนวนเต็มเป็นอาร์กิวเมนต์และส่งกลับ ไม่มีอะไร

ถ้าคุณเขียน:

typedef void (*SignalHandler)(int signum);

จากนั้นคุณสามารถประกาศsignal()เป็น:

extern  SignalHandler signal(int signum, SignalHandler handler);

นี่หมายถึงสิ่งเดียวกัน แต่โดยทั่วไปถือว่าอ่านง่ายกว่า มันเป็นที่ชัดเจนว่าการทำงานจะใช้เวลาintและและผลตอบแทนSignalHandlerSignalHandler

มันใช้เวลาเล็กน้อยในการทำความคุ้นเคย สิ่งหนึ่งที่คุณทำไม่ได้แม้ว่าจะเขียนฟังก์ชั่นตัวจัดการสัญญาณโดยใช้SignalHandler typedefในการกำหนดฟังก์ชั่น

ฉันยังอยู่ในโรงเรียนเก่าที่ชอบเรียกใช้ตัวชี้ฟังก์ชั่นเป็น:

(*functionpointer)(arg1, arg2, ...);

ไวยากรณ์สมัยใหม่ใช้เพียง:

functionpointer(arg1, arg2, ...);

ฉันเห็นได้ว่าทำไมมันถึงได้ผล - ฉันแค่อยากรู้ว่าฉันต้องมองหาว่าตัวแปรนั้นถูกเริ่มต้นแทนที่จะฟังก์ชั่นที่เรียกว่า functionpointerฉันเพียงแค่ต้องการที่จะรู้ว่าฉันต้องมองหาที่ตัวแปรจะเริ่มต้นได้มากกว่าฟังก์ชั่นที่เรียกว่า


แซมแสดงความคิดเห็น:

ฉันเคยเห็นคำอธิบายนี้มาก่อน จากนั้นเป็นกรณีนี้ฉันคิดว่าสิ่งที่ฉันไม่ได้รับคือการเชื่อมต่อระหว่างสองข้อความ:

    extern void (*signal(int, void()(int)))(int);  /*and*/

    typedef void (*SignalHandler)(int signum);
    extern SignalHandler signal(int signum, SignalHandler handler);

หรือสิ่งที่ฉันต้องการถามคืออะไรแนวคิดพื้นฐานที่หนึ่งสามารถใช้มากับรุ่นที่สองที่คุณมี พื้นฐานที่เชื่อมต่อ "SignalHandler" และตัวพิมพ์แรกคืออะไร ฉันคิดว่าสิ่งที่ต้องอธิบายที่นี่คือสิ่งที่ typedef กำลังทำอยู่ที่นี่

ลองอีกครั้ง ครั้งแรกของเหล่านี้ยกตรงจากมาตรฐาน C - ฉันพิมพ์ใหม่อีกครั้งและตรวจสอบว่าฉันมีวงเล็บขวา (ไม่จนกว่าฉันจะแก้ไขมัน - มันเป็นคุกกี้ที่ยากที่จะจำ)

ก่อนอื่นให้จำไว้ว่าtypedefแนะนำชื่อแทนสำหรับประเภท ดังนั้นนามแฝงคือSignalHandlerและประเภทของมันคือ:

ตัวชี้ไปยังฟังก์ชันที่ใช้จำนวนเต็มเป็นอาร์กิวเมนต์และส่งกลับค่าอะไร

ส่วน 'ส่งคืนอะไร' สะกดถูกvoid; อาร์กิวเมนต์ที่เป็นจำนวนเต็มคือ (ฉันเชื่อ) อธิบายตนเอง สัญกรณ์ต่อไปนี้เป็นเพียงแค่ (หรือไม่) วิธีที่ C คาถาชี้ไปที่ฟังก์ชั่นการรับข้อโต้แย้งตามที่ระบุและส่งกลับประเภทที่กำหนด:

type (*function)(argtypes);

หลังจากสร้างประเภทตัวจัดการสัญญาณฉันสามารถใช้มันเพื่อประกาศตัวแปรและอื่น ๆ ตัวอย่างเช่น:

static void alarm_catcher(int signum)
{
    fprintf(stderr, "%s() called (%d)\n", __func__, signum);
}

static void signal_catcher(int signum)
{
    fprintf(stderr, "%s() called (%d) - exiting\n", __func__, signum);
    exit(1);
}

static struct Handlers
{
    int              signum;
    SignalHandler    handler;
} handler[] =
{
    { SIGALRM,   alarm_catcher  },
    { SIGINT,    signal_catcher },
    { SIGQUIT,   signal_catcher },
};

int main(void)
{
    size_t num_handlers = sizeof(handler) / sizeof(handler[0]);
    size_t i;

    for (i = 0; i < num_handlers; i++)
    {
        SignalHandler old_handler = signal(handler[i].signum, SIG_IGN);
        if (old_handler != SIG_IGN)
            old_handler = signal(handler[i].signum, handler[i].handler);
        assert(old_handler == SIG_IGN);
    }

    ...continue with ordinary processing...

    return(EXIT_SUCCESS);
}

โปรดทราบวิธีการหลีกเลี่ยงการใช้printf()ในตัวจัดการสัญญาณ?

ดังนั้นเราได้ทำอะไรที่นี่ - นอกจากละเว้นส่วนหัวมาตรฐาน 4 ที่จะต้องทำให้รหัสรวบรวมได้อย่างหมดจด?

สองฟังก์ชั่นแรกคือฟังก์ชั่นที่ใช้จำนวนเต็มเดียวและไม่คืนค่าอะไรเลย อันที่จริงคนหนึ่งไม่ได้กลับมาขอบคุณอีกต่อไปexit(1);แต่อีกคนหนึ่งกลับมาหลังจากพิมพ์ข้อความ พึงระวังว่ามาตรฐาน C ไม่อนุญาตให้คุณทำในตัวจัดการสัญญาณมากนัก POSIXเป็นบิตใจกว้างมากขึ้นในสิ่งที่จะได้รับอนุญาตอย่างเป็นทางการ fprintf()แต่ไม่ได้โทรอนุมัติ ฉันยังพิมพ์หมายเลขสัญญาณที่ได้รับ ในalarm_handler()ฟังก์ชั่นค่าจะเป็นSIGALRMเช่นเดียวกับที่เป็นสัญญาณเดียวที่เป็นตัวจัดการ แต่signal_handler()อาจได้รับSIGINTหรือSIGQUITเป็นหมายเลขสัญญาณเพราะใช้ฟังก์ชั่นเดียวกันสำหรับทั้งคู่

จากนั้นฉันสร้างอาร์เรย์ของโครงสร้างที่แต่ละองค์ประกอบระบุหมายเลขสัญญาณและตัวจัดการที่จะติดตั้งสำหรับสัญญาณนั้น ฉันเลือกที่จะกังวลเกี่ยวกับ 3 สัญญาณ; ผมมักจะกังวลเกี่ยวกับSIGHUP, SIGPIPEและSIGTERMเกินไปและเกี่ยวกับว่าพวกเขาจะมีการกำหนด ( #ifdefรวบรวมเงื่อนไข) แต่ที่มีความซับซ้อนเพียงแค่สิ่งที่ ฉันอาจใช้ POSIX sigaction()แทนsignal()แต่นั่นก็เป็นอีกปัญหาหนึ่ง มายึดติดกับสิ่งที่เราเริ่มต้นด้วย

main()iterates ฟังก์ชั่นมากกว่ารายการไสที่จะติดตั้ง สำหรับตัวจัดการแต่ละตัวมันจะเรียกก่อนsignal()เพื่อดูว่ากระบวนการกำลังเพิกเฉยสัญญาณหรือไม่และในขณะนั้นให้ติดตั้งSIG_IGNเป็นตัวจัดการซึ่งทำให้มั่นใจได้ว่าสัญญาณจะถูกเพิกเฉย หากไม่เคยถูกละเลยสัญญาณก่อนหน้านี้สัญญาณจะถูกเรียกsignal()อีกครั้งคราวนี้เพื่อติดตั้งตัวจัดการสัญญาณที่ต้องการ (ค่าอื่น ๆ น่าจะ SIG_DFLเป็นตัวจัดการสัญญาณเริ่มต้นสำหรับสัญญาณ) เนื่องจากการเรียกครั้งแรกไปที่ 'สัญญาณ ()' ตั้งค่าตัวจัดการไปที่SIG_IGNและsignal()ส่งกลับตัวจัดการข้อผิดพลาดก่อนหน้านี้ค่าของคำสั่งoldหลังifจึงต้องเป็นSIG_IGN- ดังนั้นการยืนยัน (อาจเป็นได้SIG_ERR ถ้ามีอะไรผิดพลาดอย่างมาก - แต่ถ้าอย่างนั้นฉันก็จะเรียนรู้เรื่องนั้นจากการยิงยืนยัน)

จากนั้นโปรแกรมจะทำสิ่งต่าง ๆ และออกจากโปรแกรมตามปกติ

โปรดทราบว่าชื่อของฟังก์ชั่นสามารถถือเป็นตัวชี้ไปยังฟังก์ชั่นของประเภทที่เหมาะสม เมื่อคุณไม่ใช้วงเล็บการเรียกใช้ฟังก์ชันเช่นใน initializers ตัวอย่างเช่นชื่อฟังก์ชันจะกลายเป็นตัวชี้ฟังก์ชัน นี่คือเหตุผลว่าทำไมจึงสมเหตุสมผลในการเรียกใช้ฟังก์ชันผ่านpointertofunction(arg1, arg2)สัญกรณ์; เมื่อคุณเห็นalarm_handler(1)คุณสามารถพิจารณาว่าalarm_handlerเป็นตัวชี้ไปยังฟังก์ชั่นและดังนั้นจึงalarm_handler(1)เป็นการอุทธรณ์ของฟังก์ชั่นผ่านตัวชี้ฟังก์ชั่น

ดังนั้นจนถึงตอนนี้ฉันได้แสดงให้เห็นว่าSignalHandlerตัวแปรค่อนข้างตรงไปข้างหน้าเพื่อใช้ตราบใดที่คุณมีประเภทของค่าที่เหมาะสมที่จะกำหนดให้กับมันซึ่งเป็นสิ่งที่ฟังก์ชั่นจัดการสัญญาณทั้งสองมีให้

ตอนนี้เรากลับไปที่คำถาม - การประกาศสองสิ่งที่signal()เกี่ยวข้องกันอย่างไร

ตรวจสอบการประกาศที่สอง:

 extern SignalHandler signal(int signum, SignalHandler handler);

หากเราเปลี่ยนชื่อฟังก์ชั่นและประเภทดังนี้:

 extern double function(int num1, double num2);

คุณจะไม่มีปัญหาในการตีความว่านี่เป็นฟังก์ชั่นที่รับintและdoubleเป็นอาร์กิวเมนต์และส่งคืนdoubleค่า (คุณหรือไม่บางทีคุณควรจะไม่ยอมรับถ้ามันเป็นปัญหา - แต่คุณควรระวังเกี่ยวกับการถามคำถามอย่างหนัก อย่างนี้ถ้ามันเป็นปัญหา)

ตอนนี้แทนที่จะเป็นdoubleที่signal()ฟังก์ชั่นใช้SignalHandlerเป็นอาร์กิวเมนต์ที่สองของตนและจะส่งกลับอย่างใดอย่างหนึ่งเป็นผลของมัน

กลไกที่สามารถใช้เป็น:

extern void (*signal(int signum, void(*handler)(int signum)))(int signum);

จะอธิบายได้ยาก - ดังนั้นฉันอาจจะทำให้เสีย ครั้งนี้ฉันได้รับชื่อพารามิเตอร์ - แม้ว่าชื่อจะไม่สำคัญ

โดยทั่วไปใน C กลไกการประกาศเป็นเช่นนั้นถ้าคุณเขียน:

type var;

จากนั้นเมื่อคุณเขียนมันหมายถึงค่าที่กำหนดvar typeตัวอย่างเช่น:

int     i;            // i is an int
int    *ip;           // *ip is an int, so ip is a pointer to an integer
int     abs(int val); // abs(-1) is an int, so abs is a (pointer to a)
                      // function returning an int and taking an int argument

ในมาตรฐานtypedefจะถือว่าเป็นคลาสหน่วยเก็บข้อมูลในไวยากรณ์แทนที่จะเป็นstaticและexternเป็นคลาสหน่วยเก็บข้อมูล

typedef void (*SignalHandler)(int signum);

หมายความว่าเมื่อคุณเห็นตัวแปรประเภทSignalHandler(เรียกว่า alarm_handler) เรียกใช้เป็น:

(*alarm_handler)(-1);

ผลลัพธ์มีtype void- ไม่มีผลลัพธ์ และ(*alarm_handler)(-1);เป็นอุทธรณ์ของกับการโต้แย้งalarm_handler()-1

ดังนั้นถ้าเราประกาศ:

extern SignalHandler alt_signal(void);

หมายความว่า:

(*alt_signal)();

แทนค่าที่เป็นโมฆะ และดังนั้นจึง:

extern void (*alt_signal(void))(int signum);

เทียบเท่า ตอนนี้signal()มีความซับซ้อนมากขึ้นเพราะไม่เพียง แต่จะส่งกลับSignalHandlerก็ยังยอมรับทั้ง int และSignalHandlerเป็นข้อโต้แย้ง:

extern void (*signal(int signum, SignalHandler handler))(int signum);

extern void (*signal(int signum, void (*handler)(int signum)))(int signum);

ถ้านั่นยังทำให้คุณงงฉันไม่แน่ใจว่าจะช่วยได้อย่างไร - มันยังอยู่ในระดับลึกลับสำหรับฉัน แต่ฉันโตขึ้นเรื่อย ๆ กับวิธีการทำงานและสามารถบอกคุณได้ว่าถ้าคุณติดอยู่กับมันอีก 25 ปี หรือเป็นเช่นนั้นมันจะกลายเป็นเรื่องที่สองสำหรับคุณ (และอาจเร็วขึ้นเล็กน้อยถ้าคุณฉลาด)


3
ฉันเคยเห็นคำอธิบายนี้มาก่อน จากนั้นเป็นกรณีนี้ฉันคิดว่าสิ่งที่ฉันไม่ได้รับคือการเชื่อมต่อระหว่างสองข้อความ: extern void ( สัญญาณ (int, void ( ) (int))) (int); / * และ * / typedef เป็นโมฆะ (* SignalHandler) (int signum); สัญญาณภายนอกมือจับสัญญาณ (สัญญาณภายใน, ตัวจัดการสัญญาณมือจับ); หรือสิ่งที่ฉันต้องการถามคืออะไรแนวคิดพื้นฐานที่หนึ่งสามารถใช้มากับรุ่นที่สองที่คุณมี? พื้นฐานที่เชื่อมต่อ "SignalHandler" และตัวพิมพ์แรกคืออะไร ฉันคิดว่าสิ่งที่ต้องอธิบายที่นี่คือสิ่งที่ typedef กำลังทำอยู่ที่นี่ ขอบคุณ

6
คำตอบที่ดีฉันดีใจที่ฉันกลับมาที่หัวข้อนี้ ฉันไม่คิดว่าฉันเข้าใจทุกอย่าง แต่วันหนึ่งฉันจะ นี่คือเหตุผลที่ฉันชอบดังนั้น ขอบคุณ.
โตโต้

2
เพียงเพื่อเลือก nit: มันไม่ปลอดภัยที่จะเรียก printf () และเพื่อน ๆ ในตัวจัดการสัญญาณ printf () ไม่ได้เป็นผู้เข้าร่วมอีกครั้ง (โดยทั่วไปเพราะสามารถเรียก malloc () ซึ่งไม่ใช่ reentrant)
wildplasser

4
extern void (*signal(int, void(*)(int)))(int);หมายถึงฟังก์ชั่นจะกลับมาเป็นตัวชี้ฟังก์ชั่นsignal(int, void(*)(int)) void f(int)เมื่อคุณต้องการที่จะระบุตัวชี้ฟังก์ชั่นเป็นค่าตอบแทนไวยากรณ์ได้รับความซับซ้อน คุณต้องวางประเภทของค่าตอบแทนทางด้านซ้ายและรายการอาร์กิวเมนต์ทางด้านขวาในขณะที่มันอยู่ตรงกลางที่คุณกำหนด และในกรณีนี้signal()ฟังก์ชั่นจะใช้ตัวชี้ฟังก์ชั่นเป็นพารามิเตอร์ซึ่งทำให้สิ่งต่าง ๆ ซับซ้อนยิ่งขึ้น ข่าวดีก็คือถ้าคุณอ่านข้อความนี้ได้กองทัพก็อยู่กับคุณแล้ว :)
smwikipedia

1
โรงเรียนเก่าเกี่ยวกับการใช้&หน้าชื่อฟังก์ชันคืออะไร มันไม่จำเป็นเลย ไม่มีจุดหมายแม้แต่ และไม่ใช่ "โรงเรียนเก่า" อย่างแน่นอน โรงเรียนเก่าใช้ชื่อฟังก์ชันธรรมดาและเรียบง่าย
Jonathan Leffler

80

ตัวชี้ฟังก์ชันเหมือนกับตัวชี้อื่น ๆ แต่ชี้ไปยังที่อยู่ของฟังก์ชันแทนที่จะเป็นที่อยู่ของข้อมูล (บนกองหรือกอง) เช่นเดียวกับตัวชี้ใด ๆ จำเป็นต้องพิมพ์อย่างถูกต้อง ฟังก์ชั่นจะถูกกำหนดโดยค่าตอบแทนของพวกเขาและประเภทของพารามิเตอร์ที่พวกเขายอมรับ ดังนั้นเพื่ออธิบายฟังก์ชั่นอย่างสมบูรณ์คุณจะต้องรวมค่าส่งคืนและยอมรับประเภทของพารามิเตอร์แต่ละตัว เมื่อคุณพิมพ์คำจำกัดความดังกล่าวคุณให้ชื่อ 'ชื่อที่เรียกง่าย' ซึ่งทำให้ง่ายต่อการสร้างและอ้างอิงตัวชี้โดยใช้คำนิยามนั้น

ตัวอย่างเช่นสมมติว่าคุณมีฟังก์ชั่น:

float doMultiplication (float num1, float num2 ) {
    return num1 * num2; }

ดังนั้น typedef ต่อไปนี้:

typedef float(*pt2Func)(float, float);

สามารถใช้เพื่อชี้ไปที่doMulitplicationฟังก์ชั่นนี้ มันเป็นเพียงการกำหนดตัวชี้ไปยังฟังก์ชั่นที่ส่งกลับลอยและใช้เวลาสองพารามิเตอร์แต่ละประเภทลอย pt2Funcคำนิยามนี้มีชื่อที่เป็นมิตร โปรดทราบว่าpt2Funcสามารถชี้ไปที่ฟังก์ชันใดก็ได้ที่ส่งคืนค่าลอยตัวและรับเป็น 2 ครั้ง

ดังนั้นคุณสามารถสร้างตัวชี้ซึ่งชี้ไปที่ฟังก์ชัน doMultiplication ดังนี้:

pt2Func *myFnPtr = &doMultiplication;

และคุณสามารถเรียกใช้ฟังก์ชันโดยใช้ตัวชี้นี้ดังนี้:

float result = (*myFnPtr)(2.0, 5.1);

สิ่งนี้ทำให้การอ่านดี: http://www.newty.de/fpt/index.html


Psychotik ขอบคุณ! นั่นเป็นประโยชน์ ลิงค์ไปยังเว็บเพจพอยน์เตอร์ของฟังก์ชั่นนั้นมีประโยชน์จริง ๆ อ่านตอนนี้

... อย่างไรก็ตามลิงค์ newty.de นั้นดูเหมือนจะไม่พูดถึง typedefs เลย :( ดังนั้นแม้ว่าลิงค์นั้นจะยอดเยี่ยม แต่คำตอบในหัวข้อนี้เกี่ยวกับ typedefs นั้นมีค่ามาก!

11
คุณอาจต้องการที่จะทำpt2Func myFnPtr = &doMultiplication;แทนที่จะpt2Func *myFnPtr = &doMultiplication;เป็นmyFnPtrตัวชี้แล้ว
Tamilselvan

1
ประกาศ pt2Func * myFnPtr = & doMultiplication; แทน pt2Func myFnPtr = & doMultiplication; โยนคำเตือน
AlphaGoku

2
@Tamilselvan ถูกต้อง myFunPtrเป็นตัวชี้ฟังก์ชันอยู่แล้วให้ใช้pt2Func myFnPtr = &doMultiplication;
Dustin Biser

35

วิธีที่ง่ายมากที่จะเข้าใจตัวพิมพ์ฟังก์ชันของตัวชี้:

int add(int a, int b)
{
    return (a+b);
}

typedef int (*add_integer)(int, int); //declaration of function pointer

int main()
{
    add_integer addition = add; //typedef assigns a new variable i.e. "addition" to original function "add"
    int c = addition(11, 11);   //calling function via new variable
    printf("%d",c);
    return 0;
}

32

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

เท่าที่เคล็ดลับสำหรับการประกาศที่ซับซ้อนง่ายต่อการแยกวิเคราะห์เพื่อการบำรุงรักษาในอนาคต (โดยตัวคุณเองหรือคนอื่น ๆ ) ฉันแนะนำให้ทำtypedefชิ้นเล็ก ๆ และใช้ชิ้นส่วนเล็ก ๆ เหล่านั้นเป็นหน่วยการสร้างสำหรับการแสดงออกที่ซับซ้อน ตัวอย่างเช่น:

typedef int (*FUNC_TYPE_1)(void);
typedef double (*FUNC_TYPE_2)(void);
typedef FUNC_TYPE_1 (*FUNC_TYPE_3)(FUNC_TYPE_2);

ค่อนข้างมากกว่า:

typedef int (*(*FUNC_TYPE_3)(double (*)(void)))(void);

cdecl สามารถช่วยคุณด้วยสิ่งนี้:

cdecl> explain int (*FUNC_TYPE_1)(void)
declare FUNC_TYPE_1 as pointer to function (void) returning int
cdecl> explain double (*FUNC_TYPE_2)(void)
declare FUNC_TYPE_2 as pointer to function (void) returning double
cdecl> declare FUNC_TYPE_3 as pointer to function (pointer to function (void) returning double) returning pointer to function (void) returning int
int (*(*FUNC_TYPE_3)(double (*)(void )))(void )

และเป็น (ในความเป็นจริง) อย่างแท้จริงว่าฉันสร้างความบ้าคลั่งที่ข้างต้นได้อย่างไร


2
สวัสดีคาร์ลนั่นเป็นตัวอย่างและคำอธิบายที่ลึกซึ้งมาก นอกจากนี้ขอขอบคุณที่แสดงการใช้ cdecl ชื่นชมมาก

มี cdecl สำหรับ windows หรือไม่
แจ็ค

@ แจ็คฉันแน่ใจว่าคุณสามารถสร้างมันได้ใช่
Carl Norum

2
นอกจากนี้ยังมีcdecl.orgซึ่งให้ความสามารถแบบเดียวกัน แต่ออนไลน์ มีประโยชน์สำหรับเรานักพัฒนา Windows
zaknotzach

12
int add(int a, int b)
{
  return (a+b);
}
int minus(int a, int b)
{
  return (a-b);
}

typedef int (*math_func)(int, int); //declaration of function pointer

int main()
{
  math_func addition = add;  //typedef assigns a new variable i.e. "addition" to original function "add"
  math_func substract = minus; //typedef assigns a new variable i.e. "substract" to original function "minus"

  int c = addition(11, 11);   //calling function via new variable
  printf("%d\n",c);
  c = substract(11, 5);   //calling function via new variable
  printf("%d",c);
  return 0;
}

ผลลัพธ์ของสิ่งนี้คือ:

22

6

โปรดทราบว่ามีการใช้ definer แบบเดียวกัน math_func สำหรับการประกาศทั้งฟังก์ชัน

วิธีการเดียวกันของ typedef อาจใช้สำหรับโครงสร้างภายนอก (ใช้ sturuct ในไฟล์อื่น)


5

ใช้ typedefs เพื่อกำหนดประเภทที่ซับซ้อนยิ่งขึ้นเช่นตัวชี้ฟังก์ชั่น

ฉันจะใช้ตัวอย่างของการกำหนดเครื่องรัฐใน C

    typedef  int (*action_handler_t)(void *ctx, void *data);

ตอนนี้เราได้กำหนดประเภทที่เรียกว่า action_handler ซึ่งใช้พอยน์เตอร์สองตัวและส่งกลับค่า int

กำหนดเครื่องรัฐของคุณ

    typedef struct
    {
      state_t curr_state;   /* Enum for the Current state */
      event_t event;  /* Enum for the event */
      state_t next_state;   /* Enum for the next state */
      action_handler_t event_handler; /* Function-pointer to the action */

     }state_element;

ตัวชี้ฟังก์ชั่นของแอคชั่นนั้นดูเหมือนเป็นประเภทที่เรียบง่าย

ตัวจัดการเหตุการณ์ของฉันทั้งหมดควรเป็นไปตามประเภทที่กำหนดโดย action_handler

    int handle_event_a(void *fsm_ctx, void *in_msg );

    int handle_event_b(void *fsm_ctx, void *in_msg );

อ้างอิง:

การเขียนโปรแกรมผู้เชี่ยวชาญ C โดย Linden


4

นี่เป็นตัวอย่างที่ง่ายที่สุดของตัวชี้ฟังก์ชันและอาร์เรย์ตัวชี้ฟังก์ชันที่ฉันเขียนเป็นแบบฝึกหัด

    typedef double (*pf)(double x);  /*this defines a type pf */

    double f1(double x) { return(x+x);}
    double f2(double x) { return(x*x);}

    pf pa[] = {f1, f2};


    main()
    {
        pf p;

        p = pa[0];
        printf("%f\n", p(3.0));
        p = pa[1];
        printf("%f\n", p(3.0));
    }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.