在某些供应商代码中,我看到用作对象的结构,其指针作为头文件中的句柄,如下所示
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;
}
但是使用第一种方法,这个相同的函数会导致返回类型不匹配的错误???
答案 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
。每个都有一个单独的范围。它们不是同一类型。这不是你有意做过的事情。