C 结构体信息隐藏(不透明指针)

Evo*_*402 3 c struct information-hiding misra opaque-pointers

我目前对 C 结构信息隐藏的概念有点困惑。

这道题的背景是一个嵌入式c项目,对OOP的了解几乎为零。

到目前为止,我总是在相应模块的头文件中声明我的 typedef 结构。因此每个想要使用该结构的模块都知道该结构类型。

但经过 MISRA-C 检查后,我发现了中等严重性警告:MISRAC2012-Dir-4.8 - 结构的实现不必要地暴露给翻译单元。

经过一番研究后,我通过限制结构成员对私有范围的可见访问,发现了 C 结构信息隐藏的概念。

我立即尝试了一个简单的例子,如下所示:

struct_test.h

//struct _structName;

typedef struct _structName structType_t;
Run Code Online (Sandbox Code Playgroud)

struct_test.c

#include "struct_test.h"

typedef struct _structName
{
    int varA;
    int varB;
    char varC;
}structType_t;
Run Code Online (Sandbox Code Playgroud)

主程序

#include "struct_test.h"

structType_t myTest;

myTest.varA = 0;
myTest.varB = 1;
myTest.varC = 'c';
Run Code Online (Sandbox Code Playgroud)

这会产生编译器错误,即对于 main.c,myTest 的大小未知。当然是这样,main.c 只知道 structType_t 类型的结构存在,而没有其他信息。

所以我继续研究并偶然发现了不透明指针的概念。

所以我尝试了第二次尝试:

struct_test.h

typedef struct _structName *myStruct_t;
Run Code Online (Sandbox Code Playgroud)

struct_test.c

#include "struct_test.h"

typedef struct _structName
{
    int varA;
    int varB;
    char varC;
}structType_t;
Run Code Online (Sandbox Code Playgroud)

主程序

#include "struct_test.h"

myStruct_t myTest;

myTest->varA = 1;
Run Code Online (Sandbox Code Playgroud)

我收到编译器错误:取消引用指向不完整类型的指针struct _structName

显然我还没有理解这项技术的基本概念。我的主要困惑点是结构对象的数据会在哪里?

到目前为止,我的理解是指针通常指向数据类型的“物理”表示,并读取/写入相应地址上的内容。

但是使用上面的方法,我声明了一个指针 myTest 但从未设置它应该指向的地址。

我从这篇文章中得到了这个想法: 什么是 C 中的不透明指针?

在帖子中提到,访问是通过 set/get 接口方法处理的,所以我尝试添加一个类似的方法,如下所示:

void setVarA ( _structName *ptr, int valueA )
{
  ptr->varA = valueA;
}
Run Code Online (Sandbox Code Playgroud)

但这也不起作用,因为现在他告诉我这_structName是未知的......所以我只能在附加接口方法的帮助下访问该结构吗?如果是的话,我如何在我的简单示例中实现这一点?

我更大的问题仍然是我的结构对象位于内存中的位置。我只知道指针的概念:

varA - 地址:10 - 值:1

ptrA - 地址:22 - 值:10

但在这个例子中我只有

myTest - 地址:xy - 值:??

我无法理解相应myTest指针的“物理”表示位于哪里?

此外,我看不到在相对较小范围的嵌入式项目中这样做的好处,在这些项目中,我是模块的生产者和消费者。

有人可以解释一下,对于 1-2 名开发人员使用代码的中小型嵌入式项目来说,这种方法是否真的合理?目前,制作所有这些接口指针方法似乎比仅仅在头文件中声明结构要付出更多的努力。

先感谢您

Aco*_*orn 7

我的主要困惑点是结构对象的数据会在哪里?

关键是您不使用struct其他翻译单元中的表示(即其大小、字段、布局等),而是调用为您完成工作的函数。是的,您需要为此使用不透明指针。

我如何在我的简单示例中实现这一目标?

您必须将所有使用结构体字段(真正的结构体)的函数放在一个文件(实现)中。然后,在标头中,仅公开接口(您希望用户调用的函数,以及那些采用不透明指针的函数)。最后,用户将使用标头仅调用这些函数。他们将无法调用任何其他函数,也无法知道结构体内部的内容,因此尝试执行此操作的代码将无法编译(这就是重点!)。

此外,我看不到在相对较小范围的嵌入式项目中这样做的好处,在这些项目中,我是模块的生产者和消费者。

这是一种强制模块相互独立的方法。有时它用于向客户隐藏实现或能够保证 ABI 稳定性。

但是,是的,对于内部使用,它通常是一种负担(并且阻碍优化,因为一切都成为编译器的黑匣子,除非您使用 LTO 等)。public像 C++ 这样的其他语言中的/这样的语法方法private对此要好得多。

然而,如果您一定要遵循 MISRA 到这种程度(即如果您的项目必须遵循该规则,即使它只是建议性的),那么您无能为力。

有人可以解释一下,对于 1-2 名开发人员使用代码的中小型嵌入式项目来说,这种方法是否真的合理?

那取决于你。有一些非常大的项目没有遵循这些建议,但却取得了成功。通常,私有字段的注释或命名约定就足够了。