ตัวชี้ฟังก์ชั่น Typedef?


458

ฉันเรียนรู้วิธีโหลด DLL แบบไดนามิก แต่สิ่งที่ฉันไม่เข้าใจคือบรรทัดนี้

typedef void (*FunctionFunc)();

ฉันมีคำถามสองสามข้อ หากมีใครสามารถตอบคำถามเหล่านี้ได้ฉันก็จะขอบคุณ

  1. ทำไมต้องtypedefใช้?
  2. ไวยากรณ์ดูแปลก หลังจากนั้นvoidควรจะไม่มีชื่อฟังก์ชั่นหรืออะไร? ดูเหมือนว่าฟังก์ชั่นที่ไม่ระบุชื่อ
  3. ตัวชี้ฟังก์ชั่นถูกสร้างขึ้นเพื่อเก็บที่อยู่หน่วยความจำของฟังก์ชั่นหรือไม่?

ดังนั้นฉันสับสนในขณะนี้ คุณช่วยอธิบายสิ่งต่าง ๆ ให้ฉันได้ไหม


5
ลองดูที่ลิงค์ (ส่วนสุดท้าย) learncpp.com/cpp-tutorial/78-function-pointers
enthusiasticgeek

9
ควรสังเกตว่าตั้งแต่ c ++ 11 using FunctionFunc = void (*)();สามารถใช้แทนได้ เป็นที่ชัดเจนมากขึ้นว่าคุณเพิ่งประกาศชื่อสำหรับประเภท (ตัวชี้ไปยังฟังก์ชั่น)
user362515

เพียงเพิ่มไปยัง @ user362515 รูปแบบที่ชัดเจนยิ่งขึ้นสำหรับฉันคือ:using FunctionFunc = void(void);
topspin

@topspin IIRC สองตัวนี้ไม่เหมือนกัน หนึ่งคือประเภทตัวชี้ฟังก์ชั่นอื่น ๆ เป็นประเภทฟังก์ชั่น มีการแปลงโดยปริยายนั่นคือเหตุผลว่าทำไมจึงใช้งานได้ IANA (C ++) L ดังนั้นคนเราสามารถก้าวเข้ามาและแก้ไขฉันได้ ไม่ว่าในกรณีใดถ้าตั้งใจจะกำหนดประเภทตัวชี้ฉันคิดว่าไวยากรณ์ที่*มีความชัดเจนมากขึ้น
user362515

คำตอบ:


463

typedefเป็นโครงสร้างภาษาที่เชื่อมโยงชื่อกับประเภท
คุณใช้แบบเดียวกับที่คุณใช้กับชนิดดั้งเดิมตัวอย่างเช่น

  typedef int myinteger;
  typedef char *mystring;
  typedef void (*myfunc)();

ใช้พวกเขาเช่น

  myinteger i;   // is equivalent to    int i;
  mystring s;    // is the same as      char *s;
  myfunc f;      // compile equally as  void (*f)();

ที่คุณสามารถดูคุณก็สามารถใช้ทดแทนtypedefedชื่อที่มีความหมายที่กำหนดข้างต้น

ปัญหาอยู่ที่ตัวชี้ไปยังฟังก์ชันไวยากรณ์และความสามารถในการอ่านใน C และ C ++ และtypedefสามารถปรับปรุงความสามารถในการอ่านของการประกาศดังกล่าว อย่างไรก็ตามไวยากรณ์มีความเหมาะสมเนื่องจากฟังก์ชั่น - แตกต่างจากประเภทที่เรียบง่ายอื่น ๆ อาจมีค่าตอบแทนและพารามิเตอร์ดังนั้นบางครั้งการประกาศที่มีความยาวและซับซ้อนของตัวชี้ไปยังฟังก์ชั่น

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

เพื่อตอบคำถามสามข้อของคุณ

  • ทำไมต้องใช้ typedef เพื่อความสะดวกในการอ่านรหัส - โดยเฉพาะอย่างยิ่งสำหรับตัวชี้ไปยังฟังก์ชั่นหรือชื่อโครงสร้าง

  • ไวยากรณ์ดูแปลก (ในตัวชี้ไปยังฟังก์ชันการประกาศ) ไวยากรณ์นั้นไม่ชัดเจนในการอ่านอย่างน้อยเมื่อเริ่มต้น การใช้การtypedefประกาศแทนทำให้การอ่านง่ายขึ้น

  • ตัวชี้ฟังก์ชั่นถูกสร้างขึ้นเพื่อเก็บที่อยู่หน่วยความจำของฟังก์ชั่นหรือไม่? ใช่ตัวชี้ฟังก์ชั่นเก็บที่อยู่ของฟังก์ชั่น สิ่งนี้ไม่เกี่ยวกับสิ่งtypedefก่อสร้างที่ทำให้การเขียน / อ่านโปรแกรมง่ายขึ้นเท่านั้น คอมไพเลอร์เพิ่งขยายนิยาม typedef ก่อนที่จะรวบรวมรหัสจริง

ตัวอย่าง:

typedef int (*t_somefunc)(int,int);

int product(int u, int v) {
  return u*v;
}

t_somefunc afunc = &product;
...
int x2 = (*afunc)(123, 456); // call product() to calculate 123*456

6
ในตัวอย่างสุดท้ายไม่เพียง 'สแควร์' อ้างถึงสิ่งเดียวกันเช่นตัวชี้ไปยังฟังก์ชั่นแทนการใช้ & สแควร์
pranavk

2
คำถามในตัวอย่าง typedef ครั้งแรกของคุณคุณมีรูปแบบtypedef type aliasแต่มีคำแนะนำการทำงานมีเพียงน่าจะเป็น 2 typedef typeข้อโต้แย้ง นามแฝงจะเป็นค่าเริ่มต้นเป็นชื่อที่ระบุในอาร์กิวเมนต์ชนิดหรือไม่
dchhetri

2
@pranavk: ใช่ squareและ&square(และแน่นอน*squareและ**square) ทั้งหมดอ้างถึงตัวชี้ฟังก์ชั่นเดียวกัน
Jonathan Leffler

6
@ user814628: มันไม่ชัดเจนเลยทีเดียวที่คุณถาม ด้วยtypedef int newnameคุณกำลังทำnewnameนามแฝงสำหรับintเข้าสู่นามแฝงสำหรับด้วยtypedef int (*func)(int)คุณกำลังทำfuncนามแฝงสำหรับint (*)(int)- ตัวชี้ไปยังฟังก์ชั่นการintโต้แย้งและคืนintค่า
Jonathan Leffler

10
ฉันเดาว่าฉันแค่สับสนเกี่ยวกับการสั่งซื้อ ด้วย typedef int (*func)(int)ฉันเข้าใจว่า func เป็นนามแฝงเพียงเล็กน้อยสับสนเพราะนามแฝงนั้นพันกันกับประเภท ไปตามtypedef int INTเป็นตัวอย่างที่ผมจะมีมากขึ้นของความสะดวกถ้าชี้ฟังก์ชัน typedef typedef int(*function)(int) FUNC_1เป็นรูปแบบ ด้วยวิธีนี้ฉันสามารถเห็นประเภทและนามแฝงในสองโทเค็นที่แยกกันแทนที่จะถูกสอดเป็นหนึ่ง
dchhetri

188
  1. typedefใช้สำหรับประเภทนามแฝง ในกรณีนี้คุณกำลัง aliasing เพื่อFunctionFuncvoid(*)()

  2. อันที่จริงไวยากรณ์นั้นดูแปลก ๆ ลองดูที่:

    typedef   void      (*FunctionFunc)  ( );
    //         ^                ^         ^
    //     return type      type name  arguments
  3. ไม่นี่เป็นการบอกคอมไพเลอร์ว่า FunctionFuncชนิดนั้นจะเป็นตัวชี้ฟังก์ชัน แต่มันไม่ได้นิยามอย่างใดอย่างหนึ่งดังนี้:

    FunctionFunc x;
    void doSomething() { printf("Hello there\n"); }
    x = &doSomething;
    
    x(); //prints "Hello there"

27
typedefไม่ได้ประกาศเป็นชนิดใหม่ คุณสามารถมีtypedefชื่อ -defined หลายประเภทเดียวกันและไม่แตกต่างกัน (เช่น wrt. overloading ฟังก์ชั่น) มีสถานการณ์บางอย่างที่เกี่ยวกับวิธีที่คุณสามารถใช้ชื่อได้ชื่อtypedef-defined นั้นไม่เทียบเท่ากับชื่อที่กำหนดไว้อย่างแน่นอน แต่typedefชื่อ -defined หลายชื่อที่เหมือนกันนั้นจะเทียบเท่า
ไชโยและ hth - Alf

อ่าฉันเข้าใจแล้วตอนนี้ ขอบคุณ
Jack Harvin

4
+1 สำหรับคำตอบของคำถาม 2 - ชัดเจนมาก ส่วนที่เหลือของคำตอบก็ชัดเจนเช่นกัน แต่สิ่งหนึ่งที่โดดเด่นสำหรับฉัน :-)
Michael van der Westhuizen

33

หากไม่มีtypedefคำใน C ++ การประกาศจะประกาศตัวแปรFunctionFuncvoidประเภทตัวชี้ไปยังฟังก์ชั่นของการขัดแย้งใดกลับมา

ด้วยtypedefมันแทนกำหนดFunctionFuncเป็นชื่อสำหรับประเภทนั้น


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

9

หากคุณสามารถใช้ C ++ 11 คุณอาจต้องการใช้std::functionและusingคำหลัก

using FunctionFunc = std::function<void(int arg1, std::string arg2)>;

1
หากไม่มีusingคำหลักC ++ 11 จะเป็นเช่นtypedef std::function<void(int, std::string)> FunctionFunc;นั้นในกรณีที่มีคนต้องการห่อหุ้มอีกรอบฟังก์ชั่นที่ไม่มี C ++ 11
Top-Master

2
#include <stdio.h>
#include <math.h>

/*
To define a new type name with typedef, follow these steps:
1. Write the statement as if a variable of the desired type were being declared.
2. Where the name of the declared variable would normally appear, substitute the new type name.
3. In front of everything, place the keyword typedef.
*/

// typedef a primitive data type
typedef double distance;

// typedef struct 
typedef struct{
    int x;
    int y;
} point;

//typedef an array 
typedef point points[100]; 

points ps = {0}; // ps is an array of 100 point 

// typedef a function
typedef distance (*distanceFun_p)(point,point) ; // TYPE_DEF distanceFun_p TO BE int (*distanceFun_p)(point,point)

// prototype a function     
distance findDistance(point, point);

int main(int argc, char const *argv[])
{
    // delcare a function pointer 
    distanceFun_p func_p;

    // initialize the function pointer with a function address
    func_p = findDistance;

    // initialize two point variables 
    point p1 = {0,0} , p2 = {1,1};

    // call the function through the pointer
    distance d = func_p(p1,p2);

    printf("the distance is %f\n", d );

    return 0;
}

distance findDistance(point p1, point p2)
{
distance xdiff =  p1.x - p2.x;
distance ydiff =  p1.y - p2.y;

return sqrt( (xdiff * xdiff) + (ydiff * ydiff) );
}

1
ภายใต้สถานการณ์ที่จำเป็นต้องใช้ '&' คืออะไร ตัวอย่างเช่น 'func_p = & findDistance' และ 'func_p = findDistance' ทำงานได้ดีเท่ากันหรือไม่
เอก

1
ชื่อฟังก์ชั่นเป็นตัวชี้ดังนั้นคุณไม่จำเป็นต้องใช้ '&' กับมัน กล่าวอีกนัยหนึ่ง '&' เป็นตัวเลือกเมื่อพิจารณาพอยน์เตอร์ของฟังก์ชัน อย่างไรก็ตามสำหรับประเภทข้อมูลดั้งเดิมคุณจำเป็นต้องใช้ '&' เพื่อรับที่อยู่
Amjad

1
มันน่าประหลาดใจเสมอที่ '&' สามารถเลือกได้ หากใช้ typedef เพื่อสร้างตัวชี้ไปยังดึกดำบรรพ์ (กล่าวtypedef (*double) pคือ '&' เป็นตัวเลือกหรือไม่
Donna

ฉันคิดว่าคุณต้องการพิมพ์typedef double* pเพื่อกำหนดตัวชี้ให้เป็นสองเท่า หากคุณต้องการเติมพอยน์เตอร์pจากตัวแปรดั้งเดิมคุณจะต้องใช้ปุ่ม '&' หมายเหตุด้านข้างเราคุณ * <ชื่อตัวชี้> เพื่อยกเลิกการอ้างอิงตัวชี้ *จะใช้ในการชี้ฟังก์ชันการ dereference ตัวชี้ (ชื่อฟังก์ชั่น) และไปที่บล็อกของหน่วยความจำที่คำแนะนำการใช้ฟังก์ชั่นที่มีอยู่
Amjad

2

สำหรับกรณีทั่วไปของไวยากรณ์คุณสามารถดูภาคผนวก A ของมาตรฐาน ANSI Cการมาตรฐาน

ในรูปแบบ Backus-Naur จากตรงนั้นคุณจะเห็นว่าtypedefมีประเภทstorage-class-specifierมีชนิด

อยู่ในประเภท declaration-specifiersคุณจะเห็นว่าคุณสามารถผสม specifier ได้หลายประเภทลำดับที่ไม่สำคัญ

ตัวอย่างเช่นมันถูกต้องที่จะพูด

long typedef long a;

เพื่อกำหนดประเภทaเป็นนามแฝงสำหรับlong longเป็นชื่อแทนสำหรับดังนั้นเพื่อทำความเข้าใจกับ typedef ในการใช้งานอย่างละเอียดคุณจำเป็นต้องศึกษารูปแบบ backus-naur ที่กำหนดไวยากรณ์ (มีไวยากรณ์ที่ถูกต้องมากมายสำหรับ ANSI C ไม่ใช่เฉพาะ ISO)

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

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