基于可变大小结构的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();
}