拥有一个没有定义的结构意味着什么?

问题描述 投票:0回答:1

最近,我在系统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_;

我习惯于看到指向这样向前声明的结构的指针:extern struct _IO_FILE *stdin;,但是拥有裸露的结构似乎很奇怪,因为您不能使用该结构或将其传递给函数。这只是一个禁忌症吗?

c struct forward-declaration
1个回答
2
投票

代码struct _IO_FILE_plus;是名称_IO_FILE_plus的声明,因此,如果编译器看到某处正在使用它,它就会知道在某个点上将有一个实际描述其成员的定义。

extern修饰符表示命名的符号是某个其他编译单元中存在的外部符号。代码如:

extern struct _IO_FILE_plus _IO_2_1_stdin_;

也是符号的声明,在这种情况下为_IO_2_1_stdin_,它告诉编译器该符号是已定义的外部符号,并且存在于其他编译单元(文件)中,并且该符号的类型是本例为struct _IO_FILE_plus

[但是通常在其他声明中使用struct声明通常会使用指向struct的指针,因为struct的大小及其布局不能仅由诸如struct _IO_FILE_plus;之类的声明来确定。] >

但是在这种情况下,因为它是外部的,除非源代码中包含某些声明,要求使用编译器以这种方式使用struct的大小和布局,此声明的符号才能起作用。

因此,如果您有以下陈述的来源:

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

这些将产生错误,因为编译器需要struct _IO_FILE_plus的定义才能确定sizeof()的结果或在这些语句中为struct分配复制的内存量。

但是,如果您有这样的陈述:

struct _IO_FILE_plus *myIO = &_IO_2_1_stdin_;

这将进行编译,因为编译器仅需要知道如何查找外部变量的地址并将该地址放入指针变量。当加载应用程序并将其设置为运行时,外部变量的地址由加载器固定。

如果外部不存在,则在链接时会出现“无法解析的外部符号”错误。

API库示例

这可能有用的一种方法是,如果您有几个不同的对象或代理对象代表的设备,并且您具有一个允许人们选择目标对象或设备中的功能的功能库。

因此,您要做的是在库中将这些对象或代理对象公开为外部对象,但仅通过提供声明将其内部对象保密。

然后在功能接口中,您需要一个指向要与该功能一起使用的适当对象或代理对象的指针。

这种方法的优点是,其他有权访问您的库内部的人员可以提供其他与您的库一起使用但具有自己的代理对象的代理对象。

struct定义包含指向钩子函数的指针时,此函数特别好,您的库将调用该钩子函数来执行第三方了解但您不必执行的设备特定的操作。挂钩函数具有已定义的接口,带有一组预期结果,该接口的完成方式取决于挂钩函数的提供者。

因此库源文件:

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

然后在库随附的头文件中,您具有以下语句:

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

并且在使用这些文件的源文件中,您将具有如下语句:

int k = funcOne(&_IO_2_1_stdin_, 5);

并且作为库设计的一部分,很可能会使用C预处理器宏来进一步隐藏其中的一些管道。

© www.soinside.com 2019 - 2024. All rights reserved.