【问题标题】:Where to declare structures在哪里声明结构
【发布时间】:2019-07-15 20:05:10
【问题描述】:

我制作了 3 个文件来组织我的代码。一个main.c、一个functions.h和一个functions.c。我还制作了一个与某些函数一起使用的结构,但我遇到的问题是我找不到任何方法来声明使用 .h 文件中的结构的函数。它起作用的唯一方法是当我将函数与结构一起放在 .c 中而不在 .h 中声明它们中的任何一个。

我试过了

主要是:

#include "functions.c"

在.c:

#include "functions.h"
void myfunction(sinWave *sw, float u, float ts){
};

typedef struct{
   float amplitude;
   float fase;
} sinWave;

在.h中:

typedef struct sinWave;

void myfunction(sinWave *, float, float);

但它不起作用。我明白了:

warning: useless storage class specifier in empty declaration
'typedef struct sinWave;'a

unknown type name 'sinWave'
'void ePLL(sinWave , float , float);'

【问题讨论】:

  • #include.c 文件一起使用很少是正确的。您通常只包含 .h 文件。
  • 可能有特殊情况需要以不同寻常的方式做事,但这不是其中之一。将结构声明和函数原型放在 .h 文件中,将函数定义放在 .c 文件中,其中包括 .h。此外,您可能希望使用 double 而不是 float,除非您有充分的理由不这样做。
  • 您使用的是什么平台(Linux、Windows 或...)?什么构建系统(Makefile、SCons、Visual Studio IDE 或...)?还是您在命令行手动调用编译器?这里的大多数(全部?)答案都假设您使用的是构建系统。如果您(例如)只是从命令行调用 GCC 并指定单个 .c 文件,然后运行生成的 a.out 或 a.exe 程序,那么您将希望开始使用将链接多个目标文件转换为单个可执行文件。您可以在没有构建系统的情况下从命令行执行相同操作,但构建系统更好。
  • Lee Daniel,我打算把它放在一个 32 位 CPU 的 esp32 上,我还做了另一个实验,在这种情况下双精度对我没有帮助。
  • 我在 Windows 上,我正在编译 gcc main.c -o main.exe

标签: c function struct header-files declaration


【解决方案1】:

按正常方式做:

// functions.h
// type declaration
typedef struct { . . . } sinWave;

// function prototype
void myfunction(sinWave *, float, float);

// functions.c
#include "functions.h"

void myfunction(sinWave *sw, float u, float ts) {
   // function definition here
}

【讨论】:

  • 将结构的定义放在functions.h中会将其暴露给main.c和其他翻译单元,如果其他翻译单元不需要知道其内容,这通常是不可取的。最好用标签来标识它,并将完整的定义隔离到functions.h中。
  • 是的。这仍然是在 C 中执行此操作的“正常”方式,它始终更关注性能,而不是信息隐藏等细节。
  • 声明指向结构的指针而不定义结构是完全正常的。将其定义为“正常”并没有什么可使其成为首选的。 OP 显示的代码没有公开定义,也没有请求公开定义,因此不应该在解决方案中添加公开定义。
【解决方案2】:

这样更好:

main.c:

#include "functions.h"

int main() {
    // ...
}

functions.h:

typedef struct {
    float amplitude;
    float fase;
} sinWave;

void myfunction(sinWave *, float, float);

functions.c:

#include "functions.h"

void myfunction(sinWave *sw, float u, float ts) {
    // ...
};

像这样构建(假设您将使用sincos 等):

gcc -o xyz main.c functions.c -lm

【讨论】:

  • 将结构的定义放在functions.h中会将其暴露给main.c和其他翻译单元,如果其他翻译单元不需要知道其内容,这通常是不可取的。最好用标签来标识它,并将完整的定义隔离到functions.h中。
  • 是的,我们需要了解更多关于 main 的信息才能拨打电话。
  • 问题中的原始代码并没有尝试定义functions.h中的结构,只是引用它,因此解决方案不应添加这种未经请求的暴露。
  • @EricPostpischil,在他们明显的掌握水平上,OP 的意图似乎不太可能是使用不透明的结构类型。尽管我们无法确定,但将结构的定义放入 functions.h 并因此将其暴露给 main 正是我预期为服务于 OP 可能的预期用途所需要的。
  • @GabrielMachado 一句俗语:“优秀的开发人员不会犯错误,因为他们已经犯了所有错误。”如果你喜欢你所做的并认为隐藏一些结构、功能等会很好。在这里问,他们会提供很多建议!
【解决方案3】:

不要包含.c,是很糟糕的风格,让它在标题中可见

functions.h:

typedef struct sinWave sinWave;

void myfunction(sinWave *, float, float);

functions.c:

#include "functions.h"

struct sinWave {
   float amplitude;
   float fase;
};

void myfunction(sinWave *sw, float u, float ts) {

}

main.c:

#include "functions.h"

【讨论】:

  • typedef 本身根本没有解决任何问题(尽管在这里也不会造成任何特别的麻烦)。前向声明 struct type 可能 并不能满足 OP 的需求,尤其是未按此答案所示定位。
  • @JohnBollinger ...好吧,OP也使用typedef,这就是为什么使用typedef,问题是sinWave在函数原型中是未知的,这是一种让它可见的方法,答案有什么问题?
  • 问题是struct sinWave 类型的定义需要在需要知道其大小或需要访问其成员的范围内。在范围内只有前向声明的情况下,它是“不完整类型”。在您的示例代码中,由于其位置,struct sinWave 的定义仅适用于以下分号。
  • 据我在他的代码中看到,OP不需要知道大小(使用不完整的类型就可以了)
  • 如果他想声明该类型的对象,他需要知道它(以便将指向该对象的指针作为参数传递给函数)。如果要访问结构成员,该函数也需要范围内的完整类型,这似乎是意图。
【解决方案4】:

typedef struct sinWave; 没有声明类型。它引入了sinWave 作为结构标签。标签本身不是类型名称;它使struct sinWave 成为一种类型,但不是sinWave 本身。 (在 C++ 中,它会使 sinWave 成为一个类型。)

要解决此问题,您需要进行两项更改。在functions.h中,将sinWave声明为struct sinWave的别名类型:

typedef struct sinWave sinWave;

在functions.c中,用标签插入结构的定义:

struct sinWave
{
   float amplitude;
   float fase;
};

(您可能还想正确拼写phase。)

请注意,您已经在 function.c 中定义了结构体:

typedef struct{
   float amplitude;
   float fase;
} sinWave;

这有两个问题。一个是在一个翻译单元中使用标签定义的结构与在另一个翻译单元中没有标签定义的结构不兼容。并且您需要使用标签来识别 typedef 为哪个结构命名(或者您可以在 function.h 中使用 typedef 提供结构的完整定义,但这对于其他人来说是一个不受欢迎的解决方案原因)。

另一个问题是这个定义出现在myfunction之后,但是myfunction可能需要结构的定义,所以定义必须出现在myfunction之前。

【讨论】:

  • 我来自巴西,fase 和amplitude 是葡萄牙语单词。我知道如果我想编写专业代码,我应该停止混合这样的词。谢谢你的建议。