如果在另一个 .c 文件中定义了 struct 类型,它就变成了不完整的类型?

If struct type is defined in another .c file, it becomes incomplete type?

这段代码我没问题:

#include <stdio.h>
struct foo
{
  void * data;
};

int main()
{
  printf("%ul\n", sizeof(struct foo));
}

但是一旦结构在另一个文件中声明并提供给编译器,结构就会神奇地变成不完整类型:

编辑(我没有提供所有代码):

b.h:

struct foo

b.c:

#include "b.h"
struct foo
{
    void * data;
};

a.c:

#include <stdio.h>
#include "b.h"

int main()
{
    printf("%lu\n",sizeof(struct foo));
}

触发: $gcc a.c b.c

还是一样的错误:

error: invalid application of ‘sizeof’ to incomplete type ‘struct foo’
  printf("%lu\n",sizeof(struct foo));

编译器不使用多个源文件。每个源文件都独立编译成目标文件。在编译阶段,a.cb.c 彼此未知。所以 a.c 无法编译成对象,因为它取决于对 struct foo 是什么的了解。

这是头文件的用武之地。它可用于定义将由不同源文件共享的公共对象,然后您将该头文件包含在需要使用它的每个源文件中.

If struct type is defined in another .c file, it becomes incomplete type?

如果在需要其定义的当前翻译单元中看不到它的定义,它就成为不完整的类型,例如在运算符 sizeof.

中使用它

在包含此代码的翻译单元中

#include <stdio.h>

int main()
{
    printf("%lu\n",sizeof(struct foo));
}

struct foo 未定义。

所以编译器自然会报错,说使用了不完整的类型。

如果使用多个翻译单元,您应该将结构定义放在 header 中。

或者,如果您只需要知道结构的大小并且它没有在其他任何地方使用,那么您可以只写

#include <stdio.h>

int main()
{
    printf("%zu\n",sizeof(struct foo { void * data; } ));
}

请注意,在上面的程序中,类型 struct foo 在 main 的块范围内定义。

编辑: 更新您的问题后,然后在此翻译单元中

#include <stdio.h>
#include "b.h"

int main()
{
    printf("%lu\n",sizeof(struct foo));
}

您在 header b.h 中的结构声明不完整(您忘记在声明后放置分号)

struct foo;
         ^^^

所以实际上与之前的演示程序相比,除了结构声明现在具有文件范围而不是块范围之外,与之前的演示程序相比没有任何变化,因为它发生在之前的程序中。编译器无法确定没有定义的结构的大小

注意这个命令中的那个

$gcc a.c b.c

文件a.cb.c是分开编译的。

一个翻译单元,即一个.c文件及其#included.h文件必须 self-standing,按要求的顺序包含编译翻译单元所需的所有 定义 声明 。即使您在 GCC 命令行上提供了几个 .c 文件,这些文件中的 每个 都被视为一个 单独的 翻译单元 .

in order表示翻译单元的C源代码可以single pass编译,也就是说编译器可以在解析程序的同时生成机器代码,绝不会在绝对必要的情况下保留在内存中,因此所有必要的声明和定义必须出现在源代码中 before 它们是需要的.

C11/C18 标准说 (6.5.3.4p1)

The sizeof operator shall not be applied to an expression that has function type or an incomplete type [...]

(6.7.2.3p4)

  1. All declarations of structure, union, or enumerated types that have the same scope and use the same tag declare the same type. Irrespective of whether there is a tag or what other declarations of the type are in the same translation unit, the type is incomplete*[129]* until immediately after the closing brace of the list defining the content, and complete thereafter.

用脚注 129 说明

[129] An incomplete type may only by used when the size of an object of that type is not needed. It is not needed, for example, when a typedef name is declared to be a specifier for a structure or union, or when a pointer to or a function returning a structure or union is being declared. (See incomplete types in 6.2.5.) The specification has to be complete before such a function is called or defined.

即你的翻译单元 a.c 由以下代码组成:

// code included from <stdio.h>
...
// code included from "b.h"
struct foo;

// rest of code in a.c
int main()
{
    printf("%lu\n",sizeof(struct foo));
}

这是 C 编译器对 struct foo 的所有了解,当它达到 sizeof(struct foo) 时, 6.7.2.3p4类型struct foo仍然不完整,产生错误。

作为修复,b.h 应该而不是 完全没用,no-op struct foo; 具有实际的结构定义:

b.h:

struct foo
{
    void * data;
};

$gcc a.c b.c一起编译时,a.cb.c仍然是分开编译的。此后,链接器将生成机器代码的目标文件抓在一起。

当你使用

printf("%lu\n",sizeof(struct foo));

sizeof 只能应用于完整类型,因为 sizeof 需要知道类型的确切大小才能获得该类型对象的字节大小。

但这还没有实现,因为 struct foo 只是 forward-declared 在包含的 b.h 文件中。

struct foo 的 forward-declaration 是允许的,但 struct foo 是一个不完整的类型,只要它没有被真正声明。这尤其与单独的 TLU 有关。

b.cstruct foo 的声明使 struct foo 成为一个完整的类型,在应用 sizeof 运算符时在 a.c 中不可见;因此错误。


可能的解决方案:

  1. struct foo的forward-declaration替换为b.h内的struct foo的声明,并省略´struct foo的声明在 b.c.

  2. #include "b.c" in a.c 在调用 printf().

    时应用 sizeof 运算符之前
  3. 只需在a.c中声明struct foo。 :-)