简体   繁体   English

typedef函数指针和extern关键字

[英]typedef function pointers and extern keyword

I'm having problems understanding the syntax of a pointer to a function using typedef. 我在使用typedef理解指向函数的指针的语法时遇到了问题。 I've read a lot of answers but still couldn't understand something. 我已经阅读了很多答案,但仍然无法理解。 I'll try to explain how I see things so you could understand my thinking. 我会试着解释一下我是如何看待事物的,这样你才能理解我的想法。

So we use typedef to give aliases to existing types for example : 因此我们使用typedef为现有类型提供别名,例如:

typedef int number;

Will make it so we could use a number same as an integer (similar to preprocessor directives - I know there're some differences like when making a typedef of a pointer). 这样我们可以使用与整数相同的数字(类似于预处理器指令 - 我知道在制作指针的typedef时会有一些差异)。 Another example : 另一个例子 :

typedef struct
{
    int num;
} MyStruct;

Will give the unnamed structure an alias named MyStruct. 将为未命名的结构提供名为MyStruct的别名。

So Here's the syntax of a pointer to function typedef: 所以这是函数typedef指针的语法:

typedef int (*pFunc)(int, int);

Maybe I'm having hard time to understand this since typedef is like it's name giving aliases to TYPES and a function is not exactly type but anyway, from my understanding this is more of a pointer to some sort of a function signature, so the first int is the returned type, the second parenthesis are to indicate what types are the arguments being passed to the function. 也许我很难理解这一点,因为typedef就像它给TYPES赋予别名的名字而且函数不是完全类型但是无论如何,根据我的理解,这更像是指向某种函数签名的指针,所以第一个int是返回的类型,第二个括号用于指示传递给函数的参数是什么类型。 Now what I don't quite understand is this part : 现在我不太明白这一部分:

(*pFunc)
  • What I think it is, is that we create a new type (using typedef) named pFunc that is a pointer and that's the role of the *. 我认为是,我们创建一个名为pFunc的新类型(使用typedef),它是一个指针,它是*的作用。 Now we could create variables of this type that will point to ANY function with the signature we described. 现在我们可以创建这种类型的变量,它将指向具有我们描述的签名的ANY函数。 Am I correct ? 我对么 ?

Ok, say I'm correct, usually pointers to some memory are declared as follow : 好吧,说我是对的,通常指向一些内存的指针声明如下:

int *p;
double *p;
.
.
.

So wouldn't it make more sense to do it as follow : 因此,按照以下方式进行操作会更有意义:

(pFunc*)

Because to me it looks like if the asterisk is before the name it looks like pFunc is a variable name of type pointer of some type and not an actual type pointer. 因为对我而言,如果星号位于名称之前,它看起来像pFunc是某种类型的类型指针的变量名,而不是实际的类型指针。

  • Can we do that ^ ? 我们能做到吗? If so, is it commonly used to put the asterisk after and not before ? 如果是这样,它是否常用于将星号放在之后而不是之前? If it's more common to put it before then why is that ? 如果它之前更常见的那么为什么呢? Because like I said when we define a pointer type we always put the asterisk after the name itself like the examples above so why is that exactly ? 正如我说的那样,当我们定义指针类型时,我们总是将星号放在名称本身之后,就像上面的例子那样,为什么会这样呢?
  • Another question regarding to this, I don't quite understand what are the job of the parenthesis around *pFunc. 关于这一点的另一个问题,我不太明白* pFunc周围括号的作用是什么。 I think that they're used to indicate that pFunc type pointer of something and if we wouldn't do put parenthesis then the return type of the signature will be of type int* instead of just int, am I correct here ? 我认为它们用于表示pFunc类型指针的某些东西,如果我们不做括号,那么签名的返回类型将是int *类型而不仅仅是int,我在这里是否正确?

Ok, another thing that bothers me about it as the order of the syntax. 好吧,另一件困扰我的事情就是语法的顺序。 So far, in all typedef definitions we had the type on the left and the alias(es) on the right. 到目前为止,在所有typedef定义中,我们左边是类型,右边是别名。

typedef int number;

typedef struct
{
    int num;
} MyStruct;

We see that the int and the struct are the types which are being on the left and the alias we gave them are on the right. 我们看到int和struct是左边的类型,我们给它们的别名是右边的。

Now, in pointers to function typedef it doesn't follow this convention. 现在,在指向函数typedef的指针中,它不遵循此约定。 We have the type returned of the function on the right then the typename in parenthesis then the type of the arguments in parenthesis, this order makes me confused after looking on how the other typedef are working on the same order. 我们在右边的函数返回了类型,然后是括号中的typename,然后是括号中的参数类型,这个顺序让我在查看其他typedef如何处理相同的顺序后感到困惑。

  • Wouldn't it make more sens to do something like this ? 做这样的事情会不会更有意义? :

    typedef int (int,int) Func; So we have a typedef first, the type we want to give alias(es) to, which in this case is a function signature which takes 2 ints and return an int, and then on the right we have the alias name. 所以我们首先得到一个typedef,我们想要给别名的类型,在这种情况下是一个函数签名,它取2个int并返回一个int,然后在右边我们有别名。 Won't it make more sense ? 它会更有意义吗? this follows the other typedef order, I just don't get the function pointer order that much .. 这跟随另一个typedef命令,我只是没有得到那么多的函数指针顺序..

  • Another question of mine : When we make a pointer to a function, what does it actually mean ? 我的另一个问题:当我们指向一个函数时,它实际意味着什么? I understand we can call this function using our alias but pointers like variables are stored in a memory address ? 我知道我们可以使用别名来调用这个函数,但像变量这样的指针存储在一个内存地址中? What is there to store for a function ? 什么是存储功能?
  • Lastly, I've read that the keyword extern has something to do with pointers to function but couldn't understand what this keyword does, could someone explain to me what it does ? 最后,我已经读过关键字extern与指向函数的指针有关但无法理解这个关键字的作用,有人可以向我解释它的作用吗?

The typedef uses the same syntax for declaring types as would normally be used for declaring values. typedef使用相同的语法来声明类型,通常用于声明值。

For instance, if we declare an int called myInt , we do: 例如,如果我们声明一个名为myIntint ,我们会:

int myInt;

If we want to declare a type called myIntType to be an int, we simply add typedef : 如果我们想将一个名为myIntType的类型声明为int,我们只需添加typedef

typedef int myIntType;

We can declare a function myFunc , as follows: 我们可以声明一个函数myFunc ,如下所示:

int myFunc(int a, int b);

Which tells the compiler that there is an actual function with that name and signature that we can call. 这告诉编译器我们可以调用具有该名称和签名的实际函数。

We can also declare a function type myFuncType by doing: 我们还可以通过以下方式声明函数类型myFuncType

typedef int myFuncType(int a, int b);

And we could do: 我们可以这样做:

myFuncType myFunc;

Which is equivalent to the previous declaration of myFunc (although this form would rarely be used). 这相当于之前的myFunc声明(尽管很少使用这种形式)。

A function is not a conventional value; 功能不是传统值; it represents a block of code with an entry point address. 它表示带有入口点地址的代码块。 Function declarations like those above are implicitly extern ; 像上面那样的函数声明是隐含的extern ; they tell the compiler that the named thing exists somewhere else. 他们告诉编译器命名的东西存在于其他地方。 However, you can take the address of a function, which is called a function pointer. 但是,您可以获取函数的地址,该函数称为函数指针。 A function pointer can point to any function with the correct signature. 函数指针可以指向具有正确签名的任何函数。 A pointer is declared by prefixing the name of the type/value with a * , so, we might try: 通过在类型/值的名称前加上*来声明指针,因此,我们可能会尝试:

int *myFuncPtr(int a, int b);

But this would be incorrect because the * binds more tightly with the int , so we have declared that myFuncPtr is a function that returns a pointer to an int . 但这是不正确的,因为*int绑定得更紧密,所以我们声明myFuncPtr是一个返回指向int的指针的函数。 We must put parens around the pointer and name to change the binding order: 我们必须在指针和名称周围放置parens以更改绑定顺序:

int (*myFuncPtr)(int a, int b);

And to declare a type, we simply add typedef to the front: 要声明一个类型,我们只需在前面添加typedef

typedef int (*myFuncPtrType)(int a, int b);

In the declaration of myInt above, the compiler allocated some memory for the variable. 在上面的myInt声明中,编译器为变量分配了一些内存。 However, if we were writing some code in a different compilation unit, and wanted to reference myInt , we would need to declare it as extern (in the referencing compilation unit) so that we reference the same memory. 但是,如果我们在不同的编译单元中编写一些代码,并且想要引用myInt ,我们需要将它声明为extern (在引用编译单元中),以便引用相同的内存。 Without the extern , the compiler would allocate a second myInt , which would result in a linker error (actually that's not quite true because C allows tentative definitions, which you shouldn't use). 如果没有extern ,编译器将分配第二个myInt ,这将导致链接器错误(实际上这不是真的,因为C允许暂定定义,您不应该使用它)。

As noted above, functions are not normal values, and are always implicitly extern . 如上所述,函数不是正常值,并且总是隐式extern However, function pointers are normal values, and need the extern if you are trying to reference a global function pointer from a separate compilation unit. 但是,函数指针正常值,如果您尝试从单独的编译单元引用全局函数指针,则需要extern

Normally, you would put extern s for your global variables (and functions) into a header file. 通常,您可以将全局变量(和函数)的extern s放入头文件中。 You would then include the header into the compilation units that contain the definitions of those variables and functions so that the compiler can make sure the types match. 然后,您可以将标头包含在包含这些变量和函数的定义的编译单元中,以便编译器可以确保类型匹配。

The syntax for a variable definition or declaration is the type followed by one or more variables possibly with modifiers. 变量定义或声明的语法是一个类型,后跟一个或多个可能带有修饰符的变量。 So some examples would be: 所以一些例子是:

 int  a, b;    // two int variables a and b
 int  *a, b;   // pointer to an int variable a and an int variable b
 int  a, *b, **c;   // int variable a, pointer to an int variable b, and pointer to a pointer to an int variable c

Notice that in all of these the asterisk modifies the variable to the right of the asterisk changing it from an int into a pointer to an int or a pointer to a pointer to an int. 请注意,在所有这些中,星号修改星号右侧的变量,将其从int更改为指向int的指针或指向int的指针。 So these might be used like: 所以这些可能会被用作:

int a, *b, **c, d;

a = 5;     // set a == 5
b = &a;    // set b == address of a
c = &b;    // set c == address of b which in this case has the address of int variable a

d = **c;   // put value of a into d using pointer to point to an int variable a
d = *b;    // put value of a into d using pointer to an int variable a
d = a;     // put value of a into d using the variable a directly

The extern statement is used to indicate that the definition of a variable is located in some other file and that the variable has global visibility. extern语句用于指示变量的定义位于某个其他文件中,并且该变量具有全局可见性。 So you can declare a variable using the extern keyword to be explicit about a variable so that the C compiler will have the information it needs to do a good level of checking when compiling. 因此,您可以使用extern关键字声明一个变量,以明确变量,以便C编译器具有在编译时进行良好检查所需的信息。 The extern indicates that the variable is actually defined with its memory allocation somewhere other than the file where the source using the variable is located. extern表示变量实际上是使用其内存分配定义的,而不是使用变量的源所在的文件。

Since a function pointer is a variable then if you have a function pointer that needs global visibility when you declare the variable for files other than the source file where it is actually defined and its memory allocated you would use the extern keyword as part of the function pointer variable declaration. 由于函数指针是一个变量,如果你有一个函数指针需要全局可见性,当你为实际定义它的源文件以及它分配的内存以外的文件声明它时,你会使用extern关键字作为函数的一部分指针变量声明。 However if it is a variable that is allocated on the stack within a function or if it is used within a struct to create a member of the struct then you would not use the extern modifier on the variable. 但是,如果它是一个函数内的栈上分配一个变量,如果它是一个内使用struct来创建的成员struct ,那么你就不会使用extern修饰符的变量。

The typedef is a very nice feature of modern C because it allows you to create an alias that amounts to a kind of halfway new type. typedef是现代C的一个非常好的功能,因为它允许您创建一个等同于一种中途新类型的别名。 To have the full capability of creating a new type really requires the class type features of C++ which allows the definition of operators for the new type as well. 要拥有创建新类型的全部功能,确实需要C ++的类类型功能,它允许为新类型定义运算符。 However typedef does provide a good way of allowing a programmer to create an alias for a type. 但是,typedef确实提供了一种允许程序员为类型创建别名的好方法。

Years ago in the older C compiler days before typedef was added to the C standard, people would often use the C Preprocessor to define macros to create an alias for a complex type. 多年前,在将typedef添加到C标准之前的旧C编译器中,人们通常会使用C预处理器来定义宏来为复杂类型创建别名。 typedef is a much cleaner way of doing it! typedef是一种更清洁的方式!

Most uses of typedef are to provide a way to make it shorter and cleaner to write a variable definition. typedef的大多数用法是提供一种方法,使其更简洁,更清晰地编写变量定义。 It is used a lot with struct definitions for that reason. 由于这个原因,它与struct定义一起使用了很多。 So you might have a struct definition like the following: 所以你可能有如下的struct定义:

typdef struct {
    int iA;
    int iB;
} MyStruct, *PMyStruct;

This will create two new aliases for the struct , one for the struct itself and one for a pointer to the struct and these might be used like: 这将创建两个新的别名struct ,一个是struct本身以及一个用于指向struct和这些可能会喜欢使用:

MyStruct  exampleStruct;
PMyStruct pExampleStrut;

pExampleStruct = &exampleStruct;

This example has the basic structure of typedef keyword, definition of the new type in terms of existing types, and name of the new type. 此示例具有typedef关键字的基本结构,根据现有类型定义新类型,以及新类型的名称。

Before typedef was added to the C standard, you would specify a tag for the struct and the result would be code that looked like this: 在将typedef添加到C标准之前,您将为结构指定标记,结果将是如下所示的代码:

struct myTagStruct {     // create a struct declaration with the tag of myTagStruct
    int a;
    int b;
};

struct myTagStruct myStruct;      // create a variable myStruct of the struct

At which pointer people would usually add a C Preprocessor define to make it easier to write as in: 在哪个指针处,人们通常会添加一个C预处理器定义,以便更容易编写,如下所示:

#define MYTAGSTRUCT  struct myTagStruct

and then use it something like: 然后使用它像:

MYTAGSTRUCT myStruct;

However the syntax of typedef for function pointers is a bit different. 但是,函数指针的typedef语法有点不同。 It is more along the lines of a function declaration where the name of the function is the actual name of the typedef or the alias that is actually used. 它更像是函数声明,其中函数的名称是typedef的实际名称或实际使用的别名。 The basic structure of a typedef for a function pointer is typedef keyword followed by the same syntax as a function prototype where the name of the function is used as the typedef name. 一的基本结构typedef一个函数指针typedef关键字后跟的语法相同的函数原型其中函数的名称被用作typedef名称。

typedef int (*pFunc)(int a1, int b1);

The parenthesizes are important because they force the compiler to interpret the source text in a particular way. 括号内容很重要,因为它们强制编译器以特定方式解释源文本。 The C compiler has rules that it uses to parse the source text and you can change the way that the C compiler interprets the source text by using parenthesizes. C编译器具有用于解析源文本的规则,您可以通过使用括号来更改C编译器解释源文本的方式。 The rules have to do with the parsing and how the C compiler locates the variable name and then determines the type of the variable by using rules about left and right associativity. 规则与解析以及C编译器如何定位变量名称有关,然后通过使用有关左右关联性的规则来确定变量的类型。

a = 5 * b + 1;     // 5 times b then add 1
a = 5 * (b + 1);   // 5 times the sum of b and 1

int *pFunc(int a1, int b1);      // function prototype for function pFunc which returns a pointer to an int
int **pFunct(int a1, int b1);    // function prototype for function pFunc which returns a pointer to a pointer to an int
int (*pfunc)(int a1, int b1);    // function pointer variable for pointer to a function which returns an int
int *(*pFunc)(int a1, int b1);  // function pointer variable for pointer to a function which returns a pointer to an int

A function prototype is not a function pointer variable. 函数原型不是函数指针变量。 The syntax of a typedef is similar to the syntax for a variable definition that is not using a typedef . typedef的语法类似于不使用typedef的变量定义的语法。

typedef  int * pInt;    // create typedef for pointer to an int
int *a;                 // create a variable that is a pointer to an int
pInt b;                 // create a variable that is a pointer to an int
typedef int (*pIntFunc)(int a1, int b1);    // create typedef for pointer to a function
int (*pFuncA)(int a1, int b1);           // create a variable pFuncA that is a pointer to a function
pIntFunc  pFuncB;                        // create a variable pFuncB that is a pointer to a function

So what does it mean to have a pointer to a function? 那么指向一个函数是什么意思呢? A function entry point has an address because a function is machine code that is located at a particular memory area. 函数入口点具有地址,因为函数是位于特定存储区域的机器代码。 The address of the function is where the execution of the functions machine code is supposed to start. 函数的地址是应该开始执行函数机器代码的地方。

When the C source code is compiled, a function call is translated into a series of machine instructions which jump to the address of the function. 编译C源代码时,函数调用被转换为一系列机器指令,这些指令跳转到函数的地址。 The actual machine instructions are not really a jump but are instead a call instruction which saves the return address before it makes the jump so that when the called function completes it can do a return back to where it was called from. 实际的机器指令实际上并不是一个跳转,而是一个调用指令,它在跳转之前保存返回地址,这样当被调用函数完成时它可以返回到调用它的位置。

A function pointer variable is used like a function statement. 函数指针变量用作函数语句。 The difference between the two is similar to the difference between an array variable and a pointer variable. 两者之间的差异类似于数组变量和指针变量之间的差异。 An array variable is treated like a constant pointer to a variable by most C compilers. 大多数C编译器将数组变量视为指向变量的常量指针。 A function name is treated like a constant pointer to a function by most C compilers. 函数名称被视为大多数C编译器的函数常量指针。

What a function pointer does give you though is flexibility though it is flexibility that as with any great power can also lead to great ruin. 函数指针给你带来的是灵活性虽然它具有灵活性,但任何强大的功能都可能导致巨大的毁灭。

One use of function pointer variables is to pass a function address as an argument to another function. 函数指针变量的一个用途是将函数地址作为参数传递给另一个函数。 For instance the C Standard library has a couple of sort functions that require an argument of a collation function for comparing two elements being sorted. 例如,C标准库有几个排序函数,它们需要一个排序函数的参数来比较被排序的两个元素。 Another example would be a threading library that when you create a thread, you specify the address of the function to be executed as a thread. 另一个例子是一个线程库,当你创建一个线程时,你指定要作为一个线程执行的函数的地址。

Another case is to provide some kind of interface that hides implementation details. 另一种情况是提供某种隐藏实现细节的界面。 Let's say that you have a print function that you want to use for several different output sinks or places where the output to go, say a file, a printer, and a terminal window. 假设您有一个打印功能,您想要用于几个不同的输出接收器或输出的位置,例如文件,打印机和终端窗口。 This is similar in nature to how virtual functions are implemented by C++ compilers or how COM objects are implemented through a COM interface. 这在本质上类似于C ++编译器如何实现虚函数或COM对象如何通过COM接口实现。 So you could do something like the following which is a very simple example missing details: 所以你可以做类似下面的事情,这是一个非常简单的例子,缺少细节:

typedef struct {
    int  (*pOpenSink) (void);
    int  (*pPrintLine) (char *aszLine);
    int  (*pCloseSink) (void);
} DeviceOpsStruct;

DeviceOpsStruct DeviceOps [] = {
   {PrinterOpen, PrinterLine, PrinterClose},
   {FileOpen, FileLine, FileClose},
   {TermOpen, TermLine, TermClose}
};

int OpenDevice (int iDev)
{
    return DeviceOps[iDev].pOpenSink();
}

int LineDevice (int iDev, char *aszLine)
{
    return DeviceOps[iDev].pPrintLine (aszLine);
}
int CloseDevice (int iDev)
{
    return DeviceOps[iDev].pCloseSink();
}

Just to make clear the explanation given by others, in C/C++, the parenthesis are right associative, therefore the following declaration: 为了明确其他人给出的解释,在C / C ++中,括号是右关联的,因此以下声明:

typedef int *pFunc(int, int);

is equivalent to: 相当于:

typedef int *(pFunc(int, int));

which would be the declaration prototype of a function returning a pointer to an integer and not the declaration of a pointer to a function returning an integer. 这将是函数的声明原型,返回指向整数的指针,而不是指向返回整数的函数的指针的声明。

This is why you need to write the parenthesis around (*pFunc) to break the right association and tell the compiler that pFunc is a pointer to a function and not simply a function. 这就是为什么你需要在(*pFunc)周围编写括号来打破正确的关联并告诉编译器pFunc是一个指向函数的指针,而不仅仅是一个函数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM