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

2024-05-11

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

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

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

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

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

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

struct_test.h

//struct _structName;

typedef struct _structName structType_t;

struct_test.c

#include "struct_test.h"

typedef struct _structName
{
    int varA;
    int varB;
    char varC;
}structType_t;

main.c

#include "struct_test.h"

structType_t myTest;

myTest.varA = 0;
myTest.varB = 1;
myTest.varC = 'c';

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

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

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

struct_test.h

typedef struct _structName *myStruct_t;

struct_test.c

#include "struct_test.h"

typedef struct _structName
{
    int varA;
    int varB;
    char varC;
}structType_t;

main.c

#include "struct_test.h"

myStruct_t myTest;

myTest->varA = 1;

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

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

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

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

我从这篇文章中得到了这个想法:C 中的不透明指针是什么? https://stackoverflow.com/questions/7553750/what-is-an-opaque-pointer-in-c

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

void setVarA ( _structName *ptr, int valueA )
{
  ptr->varA = valueA;
}

但这也行不通,因为现在他告诉我_structName未知... 那么我是否只能在附加接口方法的帮助下访问该结构,如果是,我如何在我的简单示例中实现这一点?

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

varA - 地址:10 - 值:1

ptrA - 地址:22 - 值:10

但在这个例子中我只有

myTest - 地址:xy - 值:??

我很难理解相应的“物理”表示在哪里myTest指针位于?

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

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

先感谢您


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

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

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

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

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

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

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

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

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

那取决于你。有very不遵循该建议却取得成功的大型项目。通常,私有字段的注释或命名约定就足够了。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C 结构体信息隐藏(不透明指针) 的相关文章

  • 使用内部构造函数实例化类

    我有一个类 其构造函数被定义为内部 这意味着我无法实例化它 虽然这可能有道理 但出于调试和研究目的 我仍然愿意做一次 是否可以通过反射来做到这一点 我知道我可以访问私有 内部成员 但是我可以调用内部构造函数吗 或者 由于构造函数没有做任何重
  • 如何使用 C# 打印 pdf

    我在 C 应用程序中使用 进程 打印 pdf 文件 但是我无法获取打印状态 我发现可以通过 System management 和 System printing 与打印机 队列进行交互 我做了很多尝试 但都出错了使用这两个命名空间但无法打
  • 在两个 .cpp 文件之间定义全局变量 [重复]

    这个问题在这里已经有答案了 如何在 A cpp 和 B cpp 之间共享 全球化 bool 变量 其中它们都不包含其他 h 文件 他们有其他联合头文件 但彼此没有 我可以在这些共享标头中定义全局变量吗 Thanks 我可以在这些共享标头中定
  • ProtoBuf-net AsReference 需要 Activator.CreateInstance 中的公共构造函数吗?

    在我的两门课程中 看起来像这样 最少 using System using System Collections Generic using System Collections using System ComponentModel us
  • 在 MVC 类上创建主键字段

    我是 MVC 和 C 新手 我只是偶然发现它并发现它很有趣 我遇到了一个不允许我继续的问题 这是我的代码 using System using System Collections Generic using System Linq usi
  • C++:获取注册表值仅给出第一个字符[重复]

    这个问题在这里已经有答案了 我试图从注册表中获取字符串值 但我只得到第一个字母 HKEY hKey char gamePath MAX PATH if RegOpenKeyEx HKEY CURRENT USER L Software Bl
  • 禁用除滚动之外的 DataGridView

    我如何配置 datagridview 以便用户只能在行中移动并使用滚动 而没有其他 如果我禁用网格不允许我使用滚动 将您的 datagridview 设置为只读 这将禁用任何编辑 dataGridView1 ReadOnly true 在你
  • 将语句插入 SQL Server 数据库

    最近几天我试图找到这个错误 但没有成功 我正在尝试在数据库中插入一个新行 一切都很顺利 没有错误 也没有程序崩溃 My INSERT声明如下 INSERT INTO Polozaj Znesek Uporabnik Cas Kupec Po
  • 当我尝试使用 AVX 功能时,Clang 生成错误

    我使用的是 Windows 10 使用 Clang 版本 5 最近安装 当我编译以下内容时 define AVX define AVX2 include
  • 如何在 C++ 的子目录中创建文件?

    这是我的代码 如何在子目录联系人中创建文件 每次创建该文件时 它都会出现在与我的程序相同的目录中 int main ofstream myfile contacts myfile open a myfile close 在构造函数中指定完整
  • 使用正则表达式匹配以“Id”结尾的单词?

    如何组合一个正则表达式来匹配以 Id 结尾的单词并进行区分大小写的匹配 试试这个正则表达式 w Id b w 允许前面的单词字符Id和 b确保Id位于单词末尾 b是字边界断言
  • 我们应该使用 Eval 还是 Databind 事件?

    当使用 Asp Net 并使用 ListView 等控件创建网站时 使用 Eval 命令是一个好习惯吗 还是应该在 databind 事件中填充文字和数据 取决于您是否想在更新事件上写回数据 在这种情况下数据绑定 如果您只想读取该数据 可以
  • C++ 析构函数:何时释放内存?

    如果我删除一个导致其析构函数被调用的对象 那么内存是在析构函数完成函数中的任何操作之前还是之后被释放 仅当最小派生类子对象被销毁后才会释放内存 所以如果你有 class Base class Derived public Base publ
  • 为什么在 .net 中使用 Invoke on Controls? [复制]

    这个问题在这里已经有答案了 可能的重复 为什么 NET不允许跨线程操作 https stackoverflow com questions 2896504 why net does not allow cross thread operat
  • 如何使用 C# 代码使用超链接的 onClick 事件?

    我正在尝试为页面中的超链接添加条件 而不是仅仅使用特定的链接 例如 a href help Tutorial html Tutorial a 我想为不同的用户显示不同的页面 例如 如果用户以管理员身份登录 他们将看到与普通用户不同的链接 我
  • 在 C# 中生成随机值

    如何使用以下命令生成随机 Int64 和 UInt64 值RandomC 中的类 这应该可以解决问题 这是一个扩展方法 因此您可以像调用普通方法一样调用它Next or NextDouble上的方法Random目的 public stati
  • 从存储过程返回 int 值并在 ASP.NET 代码中检查它以验证登录表单

    当我多次尝试但没有得到有效结果时 使此代码运行的真实顺序是什么 SQL存储过程的代码 set ANSI NULLS ON set QUOTED IDENTIFIER ON GO ALTER PROC dbo login proc usern
  • IEnumerable.比带中断的 for 循环更快吗?

    我们的代码打开表单时遇到了一些缓慢的情况 这可能是由于for循环与break这需要很长时间才能执行 我把它切换到IEnumerable Any 并看到表格很快打开 我现在试图弄清楚是否单独进行此更改会提高性能 或者是否正在访问Product
  • 停止 TcpListener 的正确方法

    我目前正在使用 TcpListener 来处理传入连接 每个连接都有一个线程用于处理通信 然后关闭该单个连接 代码如下 TcpListener listener new TcpListener IPAddress Any Port Syst
  • 无效的模板相关成员函数模板推导 - 认为我正在尝试使用 std::set

    我有一个继承自基类模板的类模板 基类模板有一个数据成员和一个成员函数模板 我想从我的超类中调用它 我知道为了消除对成员函数模板的调用的歧义 我必须使用template关键字 我必须明确引用this在超级班里 this gt base mem

随机推荐