typedef函数指针和extern关键字

时间:2021-04-21 22:30:09

I'm having problems understanding the syntax of a pointer to a function using 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.

我对使用typedef来理解函数指针的语法感到困惑。我读过很多答案,但还是听不懂。我会解释我是如何看待事物的,这样你就能理解我的想法。

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). 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. Now what I don't quite understand is this part :

也许我很难理解这个自定义类型就像它的名字给类型别名和函数不完全类型,但无论如何,从我的理解这是更多的一个指向某种函数的签名,所以第一个int返回的类型,第二个括号表示什么类型的参数被传递给函数。我不太明白的是

(*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 *. Now we could create variables of this type that will point to ANY function with the signature we described. Am I correct ?
  • 我认为,我们创建了一个名为pFunc的新类型(使用typedef),它是一个指针,是*的角色。现在我们可以创建这种类型的变量来指向任何带有我们描述的签名的函数。我正确吗?

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. 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的括号的作用是什么。我认为它们是用来表示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.

另一件让我困扰的事是语法的顺序。到目前为止,在所有类型定义中,我们在左边有类型,在右边有别名(es)。

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. 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.

现在,在指向函数typedef的指针中,它不遵循这个约定。我们在右边返回了函数的类型然后是括号中的typename然后是括号中的参数的类型,这个顺序让我在看到其他类型def是如何处理相同的顺序后感到困惑。

  • 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. Won't it make more sense ? this follows the other typedef order, I just don't get the function pointer order that much ..

    typedef int(int,int)函数;首先我们有一个typedef,我们想要给alias(es)的类型,在这种情况下,它是一个函数签名,接收2 ints并返回int,然后在右边我们有别名。这不是更有意义吗?这是另一种类型定义顺序,我只是没有得到函数指针的顺序。

  • 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与指向函数的指针有关但我无法理解这个关键字的作用,有人能向我解释它的作用吗?

3 个解决方案

#1


6  

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:

例如,如果我们声明一个名为myInt的int,我们会:

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; 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. We must put parens around the pointer and name to change the binding order:

但这可能是错误的,因为*与int的绑定更紧密,所以我们声明myFuncPtr是一个返回指向int的指针的函数。

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. 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. 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).

在上面的myInt声明中,编译器为变量分配了一些内存。但是,如果我们在另一个编译单元中编写代码,并且想要引用myInt,我们需要将它声明为extern(在引用编译单元中),以便引用相同的内存。如果没有外部,编译器会分配第二个myInt,这会导致链接错误(实际上这并不正确,因为C允许临时定义,您不应该使用它)。

As noted above, functions are not normal values, and are always implicitly 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 externs for your global variables (and functions) into a header file. 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.

通常,您将全局变量(和函数)的externs放入头文件。然后将标头包含到包含这些变量和函数定义的编译单元中,以便编译器能够确保类型匹配。

#2


1  

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. So these might be used like:

请注意,在所有这些中,星号将变量修改为星号的右边,将它从int变为指向int的指针或指向指向int的指针的指针。

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. 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. 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语句用于指示变量的定义位于其他文件中,并且该变量具有全局可见性。因此,您可以使用extern关键字声明一个变量以显式地显示变量,以便C编译器在编译时能够获得所需的信息以进行良好的检查。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. 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.

自函数指针是一个变量,那么如果你有一个函数指针,需要全球可见性当你声明的变量文件以外的源文件,它实际上是定义和分配的内存使用extern关键字作为函数指针变量声明的一部分。但是,如果它是在函数的堆栈上分配的变量,或者如果它在结构体中用于创建结构体的成员,那么就不会在变量上使用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. 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. However typedef does provide a good way of allowing a programmer to create an alias for a type.

typedef是现代C的一个非常好的特性,因为它允许您创建一种相当于半新类型的别名。要拥有创建新类型的全部能力,确实需要c++的类类型特性,它还允许定义新类型的操作符。然而,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 is a much cleaner way of doing it!

多年前,在将typedef添加到C标准之前的旧的C编译器时代,人们常常使用C预处理器定义宏来为复杂类型创建别名。typedef是一种更简洁的方法!

Most uses of typedef are to provide a way to make it shorter and cleaner to write a variable definition. It is used a lot with struct definitions for that reason. So you might have a struct definition like the following:

typedef的大多数用途是提供一种方法,使编写变量定义变得更短、更清晰。由于这个原因,很多结构定义都使用它。你可能有一个结构定义如下

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:

这将为结构体创建两个新的别名,一个用于结构体本身,一个用于指向结构体的指针,这些别名可以如下所示:

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. 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. 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. 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编译器有用于解析源文本的规则,您可以使用括号改变C编译器对源文本的解释方式。这些规则与解析以及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. 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.

编译C源代码时,函数调用被转换为一系列机器指令,这些指令跳转到函数的地址。实际的机器指令并不是真正的跳转,而是一个调用指令,它在跳转之前保存返回地址,这样当被调用函数完成时,它就可以返回到被调用的地方。

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. A function name is treated like a constant pointer to a function by most C compilers.

函数指针变量就像函数语句一样使用。两者之间的差异类似于数组变量和指针变量之间的差异。大多数C编译器将数组变量视为指向变量的常量指针。大多数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. 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.

函数指针变量的一个用途是将函数地址作为参数传递给另一个函数。例如,C标准库有两个排序函数,它们需要一个排序函数的参数来比较被排序的两个元素。另一个例子是线程库,当您创建一个线程时,您将指定要作为一个线程执行的函数的地址。

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. So you could do something like the following which is a very simple example missing details:

另一个例子是提供一些隐藏实现细节的接口。假设您有一个打印函数,您想要将其用于几个不同的输出接收器或输出要到达的位置,例如文件、打印机和终端窗口。这与虚拟函数如何通过c++编译器实现或通过COM接口实现COM对象的方式类似。所以你可以做如下的事情这是一个非常简单的例子缺少细节:

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();
}

#3


1  

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是一个函数的指针,而不仅仅是一个函数。

#1


6  

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:

例如,如果我们声明一个名为myInt的int,我们会:

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; 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. We must put parens around the pointer and name to change the binding order:

但这可能是错误的,因为*与int的绑定更紧密,所以我们声明myFuncPtr是一个返回指向int的指针的函数。

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. 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. 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).

在上面的myInt声明中,编译器为变量分配了一些内存。但是,如果我们在另一个编译单元中编写代码,并且想要引用myInt,我们需要将它声明为extern(在引用编译单元中),以便引用相同的内存。如果没有外部,编译器会分配第二个myInt,这会导致链接错误(实际上这并不正确,因为C允许临时定义,您不应该使用它)。

As noted above, functions are not normal values, and are always implicitly 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 externs for your global variables (and functions) into a header file. 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.

通常,您将全局变量(和函数)的externs放入头文件。然后将标头包含到包含这些变量和函数定义的编译单元中,以便编译器能够确保类型匹配。

#2


1  

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. So these might be used like:

请注意,在所有这些中,星号将变量修改为星号的右边,将它从int变为指向int的指针或指向指向int的指针的指针。

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. 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. 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语句用于指示变量的定义位于其他文件中,并且该变量具有全局可见性。因此,您可以使用extern关键字声明一个变量以显式地显示变量,以便C编译器在编译时能够获得所需的信息以进行良好的检查。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. 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.

自函数指针是一个变量,那么如果你有一个函数指针,需要全球可见性当你声明的变量文件以外的源文件,它实际上是定义和分配的内存使用extern关键字作为函数指针变量声明的一部分。但是,如果它是在函数的堆栈上分配的变量,或者如果它在结构体中用于创建结构体的成员,那么就不会在变量上使用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. 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. However typedef does provide a good way of allowing a programmer to create an alias for a type.

typedef是现代C的一个非常好的特性,因为它允许您创建一种相当于半新类型的别名。要拥有创建新类型的全部能力,确实需要c++的类类型特性,它还允许定义新类型的操作符。然而,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 is a much cleaner way of doing it!

多年前,在将typedef添加到C标准之前的旧的C编译器时代,人们常常使用C预处理器定义宏来为复杂类型创建别名。typedef是一种更简洁的方法!

Most uses of typedef are to provide a way to make it shorter and cleaner to write a variable definition. It is used a lot with struct definitions for that reason. So you might have a struct definition like the following:

typedef的大多数用途是提供一种方法,使编写变量定义变得更短、更清晰。由于这个原因,很多结构定义都使用它。你可能有一个结构定义如下

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:

这将为结构体创建两个新的别名,一个用于结构体本身,一个用于指向结构体的指针,这些别名可以如下所示:

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. 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. 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. 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编译器有用于解析源文本的规则,您可以使用括号改变C编译器对源文本的解释方式。这些规则与解析以及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. 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.

编译C源代码时,函数调用被转换为一系列机器指令,这些指令跳转到函数的地址。实际的机器指令并不是真正的跳转,而是一个调用指令,它在跳转之前保存返回地址,这样当被调用函数完成时,它就可以返回到被调用的地方。

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. A function name is treated like a constant pointer to a function by most C compilers.

函数指针变量就像函数语句一样使用。两者之间的差异类似于数组变量和指针变量之间的差异。大多数C编译器将数组变量视为指向变量的常量指针。大多数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. 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.

函数指针变量的一个用途是将函数地址作为参数传递给另一个函数。例如,C标准库有两个排序函数,它们需要一个排序函数的参数来比较被排序的两个元素。另一个例子是线程库,当您创建一个线程时,您将指定要作为一个线程执行的函数的地址。

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. So you could do something like the following which is a very simple example missing details:

另一个例子是提供一些隐藏实现细节的接口。假设您有一个打印函数,您想要将其用于几个不同的输出接收器或输出要到达的位置,例如文件、打印机和终端窗口。这与虚拟函数如何通过c++编译器实现或通过COM接口实现COM对象的方式类似。所以你可以做如下的事情这是一个非常简单的例子缺少细节:

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();
}

#3


1  

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是一个函数的指针,而不仅仅是一个函数。