【问题标题】:How to achieve information hiding in this case?这种情况下如何实现信息隐藏?
【发布时间】:2019-06-07 01:17:03
【问题描述】:

我了解到 ADT 是一个重要的概念,我正在学习这项技术。

这是一个我不知道如何处理的问题。

Payload_Manager.h

typedef struct __attribute__((__packed__))
{
    u32 Addr;
    u16 Cmd;
    u16 Len;
    u8 Data[0];
}ATEIS_Payload_s;   //payload

Payload_Manager.c

#include "Payload_Manager.h"
void* Payload_Manager_New(int size)
{
    return (ATEIS_Payload_s*)malloc(size);
}
void* Payload_Manager_Ctor(void* _this, u32 ip, u16 cmdID, u16 dataLen, char* rxBuf)
{
    ATEIS_Payload_s* this = (ATEIS_Payload_s*)_this;
    this->Addr = ip;
    this->Cmd = cmdID;
    this->Len = dataLen;
    memcpy(this->Data, rxBuf, dataLen);
    return this;
}
void* Payload_Manager_Dtor(void** _this)
{
    free(*_this);
    *_this = NULL;
    return *_this;
}

DNM_Manager.h

void* DNMManager_Ctor(void* _this,
                      void* name, 
                      u32 ip, 
                      u32 sn, 
                      u32 subMask);

DNM_Manager.c

typedef struct
{
    u32 Addr;
    u32 SerialNo;
    u32 SubnetMask;
    char Name[NAME_SIZE];
}DNM;

static DNM DNMSet[SET_SIZE];
static DNM DNMTemp;

void* DNMManager_Ctor(void* _this, 
                      void* name, 
                      u32 ip, 
                      u32 sn, 
                      u32 subMask)
{
    DNM* this = (DNM*)_this;
    memcpy(this->Name, name, NAME_SIZE);
    this->Addr = ip;
    this->SerialNo = sn;
    this->SubnetMask = subMask;
    return this;
}

CmdHndlr.c

#include "Payload_Manager.h"
#include "DNM_Manager.h"
int main(void){
    ATEIS_Payload_s* pl_p = NULL;
    void* DNM_temp = NULL;

    pl_p = OSTaskQPend(0, OS_OPT_PEND_BLOCKING, &msgSize, &ts, &err);   //wait for a message

    /*This works properly*/
    DNM_temp = DNMManager_Ctor(DNM_temp,
                               &pl_p->Data[NAME],
                               pl_p->Addr,
                               *(u32*)&pl_p->Data[SN],
                               *(u32*)&pl_p->Data[SUBMASK]);
    /*following code is omitted*/
}

现在,我不希望其他文件知道类型“ATEIS_Payload_s”,除了 Payload_Manager.c 中的函数。因为只有 Payload_Manager.c 中的函数处理类型“payload”。

也就是说,我想将 CmdHndlr.c 中的代码更改为:

//#include "Payload_Manager.h"    /*no need anymore*/
#include "DNM_Manager.h"
int main(void){
    void* pl_p = NULL;    //programmer no need to know what type pl_p is
    void* DNM_temp = NULL;

    pl_p = OSTaskQPend(0, OS_OPT_PEND_BLOCKING, &msgSize, &ts, &err);   //wait for a message

    DNM_temp = DNMManager_Ctor(DNM_temp, pl_p);    //DNM_Manager_Ctor will deal with it.
    /*following code is omitted*/
}

这里有一点值得注意:函数“DNM_Manager_Ctor”同时处理类型“DNM”和“ATEIS_Payload_s”。

这是通过让 DNM_Manager_Ctor 知道类型“DNM”和“ATEIS_Payload_s”来实现的。这意味着将 DNM_Manager.c 更改为:

typedef struct
{
    u32 Addr;
    u32 SerialNo;
    u32 SubnetMask;
    char Name[NAME_SIZE];
}DNM;
typedef struct __attribute__((__packed__))    //oops, already declared in somewhere else. Is this valid?
{
    u32 Addr;
    u16 Cmd;
    u16 Len;
    u8 Data[0];
}ATEIS_Payload_s;   //payload

void* DNMManager_Ctor(void* _this, 
                      void* _dest)
{
    DNM* this = (DNM*)_this;
    ATEIS_Payload_s* dest = (ATEIS_Payload_s*)_dest;

    /*following is omitted*/

    return result;
}

我不知道这种方式是否有效。但是这种方式显然会降低模块化,即使它是有效的。

有没有更好的办法解决?

编辑:

我是面向对象的新手。我正在尝试在 C 中实现这个概念,尽管它非常粗糙。另外,我很可能在这个程序中误用了这个概念。

【问题讨论】:

  • 必须使用void * 指针这么重要吗?使用前向结构声明更具可读性。看看FILE* API 是如何实现的。
  • 我想遵循ADT的精神,虽然我不知道我是否这样做正确
  • 请注意,在ATEIS_Payload_s* pl_p = NULL; ... pl_p->Addr, ... 中,您正在取消引用一个空指针,并且您会得到未定义的行为并且应该会出现分段错误。您应该在Ctor 中使用malloc 调用来分配内存并返回分配的内存。从void* 指针执行DNM* this = (DNM*)_this; 本质上是错误的,并且取消引用该指针是UB。如果您想在堆栈上分配内存,您必须公开内部,以便用户代码知道要分配多少内存。如果在堆上分配,则可以使用指针。
  • 添加了缺少的上下文
  • 另外,u8 Data[0]; 是 UB,C11 6.7.6.2p5 表示数组的大小必须计算为大于零的大小。您应该使用u8 Data[]; 来使用“灵活数组成员”,在旧编译器上他们使用1 作为数组大小。此外,最好使用来自stdint.h 的标识符,然后是用户定义的类型。

标签: c adt information-hiding


【解决方案1】:

你射错了目标。

你的目标不应该是把每个指针都变成void*, 因为这样你就放弃了 C 编译器可以提供的所有(有限的)类型安全。

相反,您的目标应该是向客户端隐藏结构的定义(即内部结构)。

this 文章中解释了如何执行此操作。

【讨论】:

    【解决方案2】:

    您的示例很长,因此我将尝试帮助您了解信息隐藏和广告的概念。

    在谈论信息隐藏时要记住的是,如果您将 h 文件提供给外部用户,他将能够使用您的 c 函数,而无需了解编码器是如何“实现”的,例如您的 Payload_Manager.h 用户不需要知道结构是如何构建的。他唯一需要这个模块的就是它提供的功能。因此,编写此 h 文件的正确方法是首先将 struct 移动到您的 c 文件中,并且仅将该 stuct 的 typedef 放在您的 h 文件中:

    struct __attribute__((__packed__)) ATEIS_Payload_s
    {
    u32 Addr;
    u16 Cmd;
    u16 Len;
    u8 Data[0];
    };
    

    在你的 h 文件中:

    typedef struct ATEIS_Payload_s ATEIS_Payload_s;
    

    我不确定您的创建函数为什么返回 (void*)。 想想看。如果用户使用 void* Payload_Manager_Ctor 功能 - 他想要获取有效负载。 此外,您必须在 h 文件中的 c 文件中声明所有用户想要的非静态函数。所以现在它们可以按照外部用户的意图使用。

    简而言之,H 文件只需要包含它必须具备的东西才能使用这些功能,而不是它们如何做他们所做的事情的秘诀。

    现在关于抽象数据类型,使用它们的想法是,当唯一需要知道它们是什么的人是用户时。

    考虑一个简单的链表或向量。您可以创建一个列表,其中存储的唯一数据类型是 int。但是这个实现现在限制了你的代码的可用性。如果您创建一个节点,而不是为数据携带 void*,则用户对可以放置在该列表中的内容没有限制。他可以存储指向结构、数组、字符扩展的指针。列表的功能保持不变,数据将保存在列表中。但是什么样的数据?用户需要的任何类型。因为用户知道他存储了什么,他就会知道如何正确提取和操作它。

    希望对你有帮助,祝你好运!!

    【讨论】:

    • 您发布的代码将不起作用。 __attribute__ 是 gcc attribute specifier,而不是结构标记名称。 struct __attribute__((__packed__)) { .. }; 是一个匿名结构声明,没有名字,不可能使用它。与typedef struct __attribute__((__packed__)) ATEIS_Payload_s; 相同。
    • 哦,谢谢,不知道。有没有办法让它只保存在c文件中?我现在会读到它@KamilCuk
    • 是的,大部分属性只在定义中使用,在前向结构声明中省略即可。
    • 谢谢。这就是我喜欢这个网站的原因 :) @KamilCuk
    • 当然,您甚至可以使用相同的名称typedef struct ATEIS_Payload_s ATEIS_Payload_s,因为结构标签和类型定义实际上位于不同的“命名空间”中。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-29
    相关资源
    最近更新 更多