基于可变大小结构的C语言中的不完全类型
到目前为止,我一直在使用基于可变大小结构的C语言中的不完全类型,c,typedef,incomplete-type,C,Typedef,Incomplete Type,到目前为止,我一直在使用void*作为用C封装私有数据的一种方法。其思想是:用户不应该为内部数据而烦恼,只需要请求公开的函数 因此,例如: typedef void* myPrivateType; myPrivateType createMPt(int someArg); int doSomething(myPrivateType mpt, int someOtherArg); void freeMpt(myPrivateType mpt); 这可以很好地隐藏myPrivateType的内部
void*
作为用C封装私有数据的一种方法。其思想是:用户不应该为内部数据而烦恼,只需要请求公开的函数
因此,例如:
typedef void* myPrivateType;
myPrivateType createMPt(int someArg);
int doSomething(myPrivateType mpt, int someOtherArg);
void freeMpt(myPrivateType mpt);
这可以很好地隐藏myPrivateType
的内部。但是,还有最后一个小问题:void*
是非常允许的,编译器将静默地接受任何类型的指针,并且在类型不正确的情况下不会触发任何警告。
这看起来是个小问题,但它只会增加用户不正确使用界面的可能性,并浪费大量时间尝试调试错误
因此,我现在倾向于使用而不是void*
,作为编译期间控制类型的更严格的方法
因此,前面的示例如下所示:
typedef struct foo* myPrivateType;
myPrivateType createMPt(int someArg);
int doSomething(myPrivateType mpt, int someOtherArg);
void freeMpt(myPrivateType mpt);
如您所见,几乎没有任何更改,只有typedef。它比前一个示例工作得更好,就像现在一样,如果用户提供了另一个指针而不是“myPrivateType”,编译器会抱怨,错误会立即被捕获
这是一个相当好的选择。除此之外,在代码的“private部分”(即.c
文件),我必须定义什么是struct foo
。在某些情况下,当这些内容被明确地静态定义时,它是非常简单的
但有时,
myPrivateType
的内容取决于运行时提供的某些变量,因此其大小可能会有所不同。这对于C结构来说是不好的,它应该在编译时具有定义的大小
作为一种解决方法,我可以这样做,例如typedef myPrivateType:
typedef size_t* myPrivateType;
或
这使得以后可以根据size\u t
的倍数来决定大小。但是现在,myPrivateType
更为宽松,因为任何size\u t*
指针也都适合这个法案
我想知道是否有一种方法可以将这两个属性结合起来,因为myPrivateType非常严格,因此不可能与任何其他类型的指针混淆,但是底层私有数据可以在运行时保持选择其大小的能力。如何:
struct myPrivateType { void * private; };
使用的方式与使用原始void指针的方式基本相同。您可以在头中公开整个结构,因为应用程序对它包含的void指针没有什么作用
这解决了使用void*进行隐式转换的问题,而不会在库函数中引入mpt->private解引用之外的任何刺激
编辑:使用typedef结构myPrivateType myPrivateType如果愿意,代码>构造
我想知道是否有一种方法可以组合这两个属性,myPrivateType非常严格,因此不可能与任何其他类型的指针混淆,但底层的私有数据保持了在运行时选择其大小的能力
是的,您可以很容易地通过使用:
struct foo
{
int dataType;
void* fooData;
};
struct FooData1
{
// ...
};
struct FooData2
{
// ...
};
myPrivateType createMPt(int someArg)
{
myPrivateType ret = malloc(sizeof(*ret));
if ( someArg == 10 )
{
ret->dataType = 1;
ret->fooData = malloc(sizeof(FooData1));
}
else if ( someArg == 20 )
{
ret->dataType = 2;
ret->fooData = malloc(sizeof(FooData2));
}
else
{
// ...
}
}
更新
如果公共函数(在.h文件中声明的函数)在紧循环中被多次调用,并且该函数的实现必须基于struct foo
的dataType
调用不同的函数,则可以将函数指针存储在struct foo
中。这类似于C++类中的虚拟表的使用。
struct foo
{
int dataType;
void* fooData;
void (*functionInTightLoop)(void);
};
struct FooData1
{
// ...
};
void FooData1Function()
{
}
struct FooData2
{
// ...
};
void FooData2Function()
{
}
myPrivateType createMPt(int someArg)
{
myPrivateType ret = malloc(sizeof(*ret));
if ( someArg == 10 )
{
ret->dataType = 1;
ret->fooData = malloc(sizeof(FooData1));
ret->functionInTightLoop = FooData1Function;
}
else if ( someArg == 20 )
{
ret->dataType = 2;
ret->fooData = malloc(sizeof(FooData2));
ret->functionInTightLoop = FooData2Function;
}
else
{
// ...
}
}
void tightFunction(myPrivateType foo)
{
foo->functionInTightLoop();
}
“但有时,struct foo的内容取决于运行时提供的某个变量,因此其大小可能会有所不同”-而不是在运行时。结构总是有固定的大小。每当您有动态大小的东西时,它就在堆上,然后您存储一个指针,带有。。固定尺寸;)您知道代码需要在几年后由那些以前从未见过代码的人维护吗?这意味着所有代码都应该以一种直接/简单的方式编写,因为它仍然可以完成任务。试图“隐藏”定义!您希望用户使用源代码吗?你是在努力发展工作保障吗?我可以向您保证,“隐藏”代码中的任何内容都会很快让您指向门。执行代码维护时会发生什么?一旦他们意识到你所做的事情,他们就会修改它以获得可见性。界面需要完全可见。算法的实现可以隐藏在接口后面。然后,可以在不更改接口的情况下修改实现。因此,接口(如相关头文件中所示)需要完全可见。配置控制可以轻松地隐藏实现。谢谢。是的,你说得对,它很有效。但它增加了一个间接级别。malloc/free管理要复杂得多,但更重要的是,如果要将myPrivateType
用于一些热性能循环中,额外的间接操作将在表中留下一些性能。@Cyan,.c文件必须以某种方式处理类型的差异。我给你指明了一条路。我想还有其他方法可以解决这个问题。你确定性能的改变吗?您以前通过值传递指针,现在它是一个包含值指针的结构。很有可能会完全一样asm@Jon,这取决于如何指定myPrivateType
。因为它没有显示在@Sahu示例中,所以我假设它重复使用了我示例中的一个,即:typedef struct foo*myPrivateType代码>。在这种情况下,确实有一个额外的参考。但是如果它使用您的约定typedef struct foo myPrivateType
,那么它就不再是一个不完整的类型,事实上,如果不是null,性能差异将是最小的,特别是如果void*fooData
是结构的第一个成员
@Sahu:感谢您提供的高级示例。我相信这超出了我的想法(我不需要多重dif)
struct foo
{
int dataType;
void* fooData;
void (*functionInTightLoop)(void);
};
struct FooData1
{
// ...
};
void FooData1Function()
{
}
struct FooData2
{
// ...
};
void FooData2Function()
{
}
myPrivateType createMPt(int someArg)
{
myPrivateType ret = malloc(sizeof(*ret));
if ( someArg == 10 )
{
ret->dataType = 1;
ret->fooData = malloc(sizeof(FooData1));
ret->functionInTightLoop = FooData1Function;
}
else if ( someArg == 20 )
{
ret->dataType = 2;
ret->fooData = malloc(sizeof(FooData2));
ret->functionInTightLoop = FooData2Function;
}
else
{
// ...
}
}
void tightFunction(myPrivateType foo)
{
foo->functionInTightLoop();
}