可变大小结构中C的不完整类型

时间:2015-06-21 22:44:20

标签: 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*是如此宽松,编译器将默默地接受任何类型的指针,并且在类型不正确的情况下不会触发任何警告。 这看起来像一个小问题,但它只会增加用户不正确地使用界面的可能性,并且会花费大量时间来调试错误。

因此,我现在倾向于使用incomplete types而不是void*,这是在编译期间控制类型的更严格方法。

因此前一个例子如下:

typedef struct foo* myPrivateType;

myPrivateType createMPt(int someArg);
int doSomething(myPrivateType mpt, int someOtherArg);
void freeMpt(myPrivateType mpt);

正如您所看到的,几乎没有任何变化,只有typedef。它比以前的例子效果更好,如果用户提供的另一个指针不是“myPrivateType”,编译器会抱怨,错误将立即被捕获。

这是一个相当不错的选择。除此之外,在代码的“私有部分”(.c文件)中,我将必须定义struct foo是什么。在某些情况下,当这些内容明确而静态地定义时,它非常简单。

但有时候,myPrivateType的内容取决于运行时提供的某些变量,因此其大小可能会有所不同。这对于C struct没有好处,它应该在编译时具有定义的大小。

作为一种解决方法,我可以通过这种方式输入typedef myPrivateType:

typedef size_t* myPrivateType;

typedef size_t myPrivateType[];   // equivalent

这样可以稍后决定大小,作为size_t的倍数。但现在,myPrivateType更宽松,因为任何size_t*指针也适合该法案。

我想知道是否有一种结合两种属性的方法,myPrivateType非常严格,因此不可能与任何其他类型的指针混淆,但底层私有数据保持选择其大小的能力在运行时。

2 个答案:

答案 0 :(得分:1)

怎么样:

struct myPrivateType { void * private; };

使用方式与使用原始void指针的方式完全相同。您可以在标题中公开整个结构,因为应用程序无论如何都无法使用它包含的void指针。

这解决了使用void *进行隐式转换的问题,而不会在库函数中的mpt->私有解除引用之外引入任何烦恼。

编辑:如果您愿意,可使用typedef struct myPrivateType myPrivateType;构造。

答案 1 :(得分:1)

  

我想知道是否有办法将两个属性组合在一起,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文件中声明的函数,在紧密循环中被多次调用,并且该函数的实现必须基于dataType {{1}调用不同的函数。你可以在struct foo中存储一个函数指针。这类似于在C ++类中使用虚拟表。

struct foo