这个结构定义是如何私有化的?

时间:2014-10-09 10:36:28

标签: c struct typedef

在某些供应商代码中,我看到用作对象的结构,其指针作为头文件中的句柄,如下所示

hdr.h

typedef struct _FOO_Obj_ {
    uint16_t x;
} FOO_Obj;

typedef struct FOO_Obj * FOO_Handle;

src.c

#include <hdr.h>
void main (void) {
    FOO_Handle bar = FOO_init(); // No error
    bar->x = 5;    // Error: pointer to incomplete class type
}

这允许包含此类头文件的文件使用类型FOO_Handle但以某种方式阻止访问FOO_Obj的成员,例如在src.c中标记的行...这里发生了什么?

为了比较,我之前使用过以下内容:

struct FOO_Obj {
    uint16_t x;
};
typedef struct FOO_Obj * FOO_Handle;

使用这种方法,我也可以这样做:

FOO_Obj y;
FOO_Handle bar (void) {  // unintentional bicycle pun ftw
    return &y;
}

但是使用第一种方法,这个相同的函数会导致返回类型不匹配的错误???

3 个答案:

答案 0 :(得分:2)

结构的类型为struct _FOO_Obj_,在typedef之后,它有一个别名FOO_Obj。但是,没有struct FOO_Obj这样的类型。

这是另一条typedef

typedef struct FOO_Obj * FOO_Handle;

应该是

typedef FOO_Obj * FOO_Handle;

typedef struct _FOO_Obj_ * FOO_Handle;

答案 1 :(得分:1)

我仍然不知道他们打算如何使用它,但这里有一个适用于这些东西的一般规则,如果你想超越通常的&#34;不透明的指针&#34;只使用指针struct而不定义struct

的方法

您可能在不同的编译单元(此处为用户代码和库)中有不同的struct定义,这些定义作为参数传递给单元之间的函数,但是

  • 他们的结构必须完全相同,相同类型的订单相同
  • 如果struct在一个单元中有一个标记名,那么它也必须在另一个单元中有一个
  • 如果两个单元都有struct标记,则此标记必须完全相同

否则(一个struct带有标签,一个没有或不同的标签名称)行为未定义。想想链接时间优化,看看为什么这可能很重要。

还有另一个适用于C ++的规则,我最近学到了很多困难。在那里,名称修改的C ++名称是struct标记(如果存在),或typename(如果它不存在)。因此,如果您struct没有标记,那么您最好同意typedef个名称。

您在代码中发现的另一个小问题是,以下划线开头的名称是在文件范围内保留的,不应使用它们。

答案 2 :(得分:1)

通常,只要您提到尚未存在的结构类型,就可以创建它。如果您同时指定结构的内容,则将其创建为完整类型,否则不完整。完整的结构类型允许使用.->运算符。不完整的结构类型不适用。

可以使用不执行任何其他操作的声明创建结构类型:

struct foo; /* Creates an incomplete type called "struct foo") */
struct bar { int x,y; }; /* Creates a complete type called "struct bar" */

或者它可以使用声明来创建,该声明也声明了一些变量。

struct foo *p; /* Creates an incomplete type called "struct foo"
                  and a variable of type "struct foo *" called "p" */
struct bar { int x,y; } v, *p; /* Creates a complete type called "struct foo"
                                  and a variable of type "struct foo" called "v"
                                  and a variable of type "struct foo *" called "p" */

请注意,除非struct foo v;已作为完整类型存在,否则您不能只说struct foo。不完整类型的大小未知,因此编译器不知道为p分配多少空间。但是指向结构的指针具有已知的大小,即使结构本身没有,因此struct foo *p;可以在不完整的类型下正常工作。

或者您可以在创建typedef的同时创建结构:

typedef struct foo f, *fptr; /* Creates an incomplete type with 2 names:
                                "struct foo" and "f"; also makes "fptr" an alias
                                for the type "struct foo *" */
typedef struct bar { int x,y; } b, *bptr; /* Creates a complete type with 2 names:
                                             "struct bar" and "b"; also makes "bptr"
                                             an alias for the type "struct bar *" */

同时在函数声明的参数列表中声明结构类型和该类型的变量的版本:

int dosomething(struct foo *p) { ... }
int dosomethingelse(struct foo *p) { ... }

如果struct foo已经存在,则上述代码为第一个函数创建类型struct foo,为第二个函数创建另一个类型struct foo。每个都有一个单独的范围。它们不是同一类型。这不是你有意做过的事情。