【发布时间】: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