简体   繁体   English

没有定义的结构是什么意思?

[英]What does it mean to have a struct without a definition?

Recently, I ran across the following code in my system stdio.h :最近,我在系统stdio.h中遇到了以下代码:

struct _IO_FILE_plus;
extern struct _IO_FILE_plus _IO_2_1_stdin_;
extern struct _IO_FILE_plus _IO_2_1_stdout_;
extern struct _IO_FILE_plus _IO_2_1_stderr_;

I'm used to seeing pointers to structs that are forward-declared like this: extern struct _IO_FILE *stdin;我习惯于看到指向像这样前向声明的结构的指针: extern struct _IO_FILE *stdin; , but having a bare struct seems very odd, since you can't use the struct or pass it to functions. ,但是拥有一个裸结构似乎很奇怪,因为您不能使用该结构或将其传递给函数。 Is this just a no-op?这只是一个无操作吗?

The code struct _IO_FILE_plus;代码struct _IO_FILE_plus; is a declaration of the name _IO_FILE_plus so that if the compiler sees it being used at some place, it knows that there will be a definition at some point that actually describes its members.是一个名为_IO_FILE_plus的声明,因此如果编译器看到它在某个地方被使用,它就知道在某个时候会有一个实际描述其成员的定义。

The extern modifier indicates that the symbol named is an external symbol that exists in some other compilation unit. extern修饰符表示命名的符号是存在于某个其他编译单元中的外部符号。 Code such as:代码如:

extern struct _IO_FILE_plus _IO_2_1_stdin_;

is also a declaration of the symbol, in this case _IO_2_1_stdin_ , telling the compiler that the symbol is an external symbol that is defined and exists in some other compilation unit (file) and what the type of the symbol is, in this case a struct _IO_FILE_plus .也是符号的声明,在本例中_IO_2_1_stdin_ ,告诉编译器该符号是定义并存在于其他编译单元(文件)中的外部符号,以及符号的类型是什么,在本例中为struct _IO_FILE_plus

However normally using a struct declaration in some other declaration would normally use a pointer to the struct since the size of the struct and its layout can not be determined by a mere declaration such as struct _IO_FILE_plus;但是,通常在其他声明中使用struct声明通常会使用指向该struct的指针,因为结构的大小及其布局不能仅由诸如struct struct _IO_FILE_plus; . .

However in this case since it is an external, unless the source code contains some statement which requires that the size and layout of the struct be available to the compiler, using a declared symbol in this fashion works.但是在这种情况下,由于它是外部的,除非源代码包含一些要求编译器可以使用struct的大小和布局的语句,否则以这种方式使用声明的符号是有效的。

So if you had source such as these statements:因此,如果您有诸如这些陈述之类的来源:

struct _IO_FILE_plus *myIo = malloc(sizeof(struct _IO_FILE_plus));

struct _IO_FILE_plus myIo = _IO_2_1_stdin_;  // no pointers here, struct assignment

these would generate an error because the compiler needs the definition of the struct _IO_FILE_plus in order to determine the result of sizeof() or the amount of memory to copy for struct assignment in these statements.这些会产生错误,因为编译器需要struct _IO_FILE_plus的定义来确定sizeof()的结果或 memory 的数量要复制以在这些语句中进行struct分配。

However if you have a statement such as:但是,如果您有如下声明:

struct _IO_FILE_plus *myIO = &_IO_2_1_stdin_;

this will compile because the compiler only needs to know how to find the address of the external variable and to put that address into a pointer variable.这将编译,因为编译器只需要知道如何找到外部变量的地址并将该地址放入指针变量中。 The address of the external variable is fixed up by the loader when the application is loaded and set up to be run.当应用程序被加载并设置为运行时,外部变量的地址由加载器固定。

If the external does not exist then you will get an "unresolved external symbol" error when linking.如果外部不存在,那么链接时您将收到“未解析的外部符号”错误。

API Library Example API 库示例

One way this may be useful is if you have several different objects or devices represented by proxy objects and you have a function library that you want to allow people to choose the target object or device for the functions in it.这可能有用的一种方法是,如果您有多个由代理对象表示的不同对象或设备,并且您有一个 function 库,您希望允许人们为其中的功能选择目标 object 或设备。

So what you do is in your library you expose these objects or proxy objects as externals but you keep their internals secret by only providing the declaration.因此,您所做的是在您的库中将这些对象或代理对象公开为外部对象,但您仅通过提供声明来保持其内部的秘密。

Then in the function interface you require a pointer to the appropriate object or proxy object to be used with the function.然后在 function 接口中,您需要一个指向相应 object 或代理 object 的指针以与 ZC1C4245268E67A94FD11 一起使用

The nice thing about this approach is that other parties with access to your library internals can provide additional proxy objects that work with your library but with their own proxy objects.这种方法的好处是可以访问您的库内部的其他方可以提供与您的库一起使用但使用他们自己的代理对象的其他代理对象。

This works especially well when the struct definition contains pointers to hook functions that your library would invoke to perform device specific operations that the third party knows about but you don't have to.struct定义包含指向您的库将调用以执行第三方知道但您不必执行的设备特定操作的挂钩函数的指针时,此方法尤其有效。 The hook functions have a defined interface with a set of expected results and how that is done is up to the provider of the hook function.钩子函数有一个带有一组预期结果的定义接口,如何完成取决于钩子 function 的提供者。

So library source file:所以库源文件:

struct _IO_FILE_plus {
    unsigned char  buffer[1024];
    int bufptr1;
    //  …  other struct member definitions
    int (*hookOne)(struct _IO_FILE_plus *obj);   // third party hook function pointer
    int (*hookTwo)(struct _IO_FILE_plus *obj);   // third party hook function pointer
};

struct _IO_FILE_plus _IO_2_1_stdin_ = { {0}, 0, …. };
struct _IO_FILE_plus _IO_2_1_stdout_ = { {0}, 0, …. };
struct _IO_FILE_plus _IO_2_1_stderr_ = { {0}, 0, …. };

int  funcOne (struct _IO_FILE_plus *obj, int aThing)
{
    int  iResult;

    if (obj->hookOne) iResult = obj->hookOne(obj);

    // do other funcOne() stuff using the object, obj, provided

    return iResult;
}


int  funcTwo (struct _IO_FILE_plus *obj, double aThing)
{
    int  iResult;

    if (obj->hookTwo) iResult = obj->hookTwo(obj);

    // do other funcTwo() stuff using the object, obj, provided

    return iResult;
}

The library source file compiles fine because the compiler has the full definition of the struct available.库源文件编译得很好,因为编译器具有可用struct的完整定义。 Then in the header file provided with the library you have these statements:然后在随库提供的 header 文件中,您有以下语句:

struct _IO_FILE_plus ;

extern struct _IO_FILE_plus _IO_2_1_stdin_ ;
extern struct _IO_FILE_plus _IO_2_1_stdout_ ;
extern struct _IO_FILE_plus _IO_2_1_stderr_ ;

extern int  funcOne (struct _IO_FILE_plus *obj, int aThing);
extern int  funcTwo (struct _IO_FILE_plus *obj, double aThing);

These all work because none of these source statements require the actual definition of the struct to be available to the compiler.这些都有效,因为这些源语句都不需要编译器可以使用struct的实际定义。 The compiler only needs to know that there are such symbols defined somewhere.编译器只需要知道某处定义了这样的符号。

In the source file using these you could have a statement like:在使用这些的源文件中,您可以有如下语句:

int k = funcOne(&_IO_2_1_stdin_, 5);

and again this only requires the compiler to know that the symbol exists and at some point the address of that symbol will be available.同样,这只需要编译器知道符号存在,并且在某个时候该符号的地址将可用。

And as part of the library design there may well be C Preprocessor macros used to hide some of this plumbing further.作为库设计的一部分,很可能有 C 预处理器宏用于进一步隐藏这些管道。 So you may have macros such as:所以你可能有宏,例如:

#define DO_FUNCONE(io,iVal)  funcOne(&(io), (iVal))

#define DO_FUNCONE_STDIN(iVal)  funcOne(&_IO_2_1_stdin_,(iVal))

#define IO_STDIN  (&_IO_2_1_stdin)

However a statement like the following will not compile because the compiler will be providing a copy of the struct to the function which takes the value of the external and not a pointer to it:然而,像下面这样的语句将不会编译,因为编译器将向 function 提供struct的副本,它采用外部的值而不是指向它的指针:

int k = doFuncOne (_IO_2_1_stdin_);  // compiler error. definition of struct _IO_FILE_plus not available

where the function definition of the function doFuncOne() looks like:其中 function doFuncOne()的 function 定义如下所示:

// compiler error. definition of struct _IO_FILE_plus not available
int doFuncOne (struct _IO_FILE_plus obj)  // notice this is struct and not pointer to struct
{
    // do some setup then call funcOne().
    return funcOne(&obj, 33);
}

However a change to the interface of the function doFuncOne() would allow it to compile:但是,更改 function doFuncOne()的接口将允许它编译:

// following would compile as only declaration is needed by the compiler.
int doFuncOne (struct _IO_FILE_plus *obj)  // notice this is now pointer to struct
{
    // do some setup then call funcOne().
    return funcOne(obj, 33);
}

The library could provide a version of the function funcOne() , say funcOneStruct() , which allowed an argument of the struct rather than pointer to the struct because the compiler has a definition of the struct available when compiling the source files of the library.该库可以提供 function funcOne()的版本,例如funcOneStruct() ,它允许struct的参数而不是指向struct的指针,因为编译器在编译库的源文件时具有可用struct的定义。 However people using the library would be unable to use that function because the users of the library have only the declaration of the struct available to them and not the definition of the struct .但是,使用该库的人将无法使用该 function ,因为该库的用户只有对他们可用的struct的声明,而不是struct的定义。

Such a function may be useful for third party developers who have a definition of the struct available to them perhaps to clone one of the existing objects provided by the library.这样的 function 可能对具有可用struct定义的第三方开发人员有用,他们也许可以克隆库提供的现有对象之一。

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

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