私有定义结构的公共sizeof

时间:2014-10-03 15:11:53

标签: c struct data-hiding

我有一个小数据隐藏模块,如下所示:

/** mydata.h */
struct _mystruct_t;
typedef struct _mystruct_t mystruct;

mystruct *newMystruct();
void freeMystruct( mystruct** p );

/** mydata.c */
#include "mydata.h"
struct _mystruct_t {
    int64_t data1;
    int16_t data2;
    int16_t data3;
};
// ... related definitions ... //

在大多数情况下,这就是我想要的;虽然简单,但结构有严格的一致性要求,我真的不想提供对数据成员的访问。

问题是在客户端代码中我想将结构包含在我希望在堆栈上分配的另一个结构中。现在我正在跳过箍以释放某些客户端代码中的mystruct* s。因为a)mystruct非常小,我真的不认为它会很快变大,b)如果我改变mystruct,客户端代码必须重新编译不是问题,我想将mystruct的大小公开(即在标题中)。

我考虑过两种可能性:

/** mydata.h */
typedef struct {
    // SERIOUSLY DON'T ACCESS THESE MEMBERS
    int64_t data1;
    int16_t data2;
    int16_t data3;
} mystruct;

我认为这里的缺点不言而喻。

OR

/** mydata.h */
#define SIZEOF_MYSTRUCT (sizeof(int64_t)+sizeof(int16_t)+sizeof(int16_t))
// everything else same as before...

/** mydata.c */
// same as before...
_Static_assert (SIZEOF_MYSTRUCT == sizeof(mystruct), "SIZEOF_MYSTRUCT is incorrect")

当然这看起来不太理想,因为我必须手动更新这个值,而且我不知道结构的对齐是否实际上导致这个错误(我在写这个时想到静态断言)问题,它部分解决了这个问题)。

这些是首选吗?或者甚至更好,是否有一些聪明的技巧在标题中提供实际的结构定义,而后来却以某种方式隐藏了访问成员的能力?

3 个答案:

答案 0 :(得分:1)

您可以创建分发给最终用户的不同.h文件,将您的秘密结构定义为字节数组(您无法隐藏数据加密/校验和不仅仅是说“这里有一些字节”):

typedef struct {
    unsigned char data[12];
} your_struct;

您必须确保所有编译器和选项的结构都相同,因此在库代码中使用__declspec(align())(对于VC),例如:

// Client side
__declspec(align(32)) typedef struct {
    int64_t data1;
    int16_t data2;
    int16_t data3;
} mystruct;

防止结构长16B而不是通常预期的12B。或者只使用/Zp编译器选项。

答案 1 :(得分:1)

我将继续使用生成#define的配置时间来描述mystructtypedef char[SIZEOF_MYSTRUCT] opaque_mystruct的大小,以简化mystruct占位符的创建。

可能configure time actions的想法值得一些解释。一般的想法是

  1. mystruct的定义放入私有的,未导出但仍然分布的标题中,
  2. 创建一个在库之前构建并执行的小型测试应用程序。测试应用程序将#include私有头,并为给定的编译器和编译选项打印实际sizeof (mystruct)
  3. 创建一个合适的脚本,该脚本将创建一个带有config.h的库#define SIZEOF_MYSTRUCT <calculated_number>,并可能定义opaque_mystruct。
  4. 使用体面的构建系统自动化这些步骤很方便,例如cmakegnu autotools或任何其他支持configure阶段的系统。实际上,所有提到的系统都有内置工具,可以简化整个任务,调用几个预定义的宏。

答案 2 :(得分:-1)

我一直在研究和思考,并把我的一个潜在答案带到了一个新的水平;我认为它解决了我所有的担忧。请批评。

/** in mydata.h */
typedef const struct { const char data[12]; } mystruct;
mystruct createMystruct();
int16_t exampleMystructGetter( mystruct *p );
// other func decls operating on mystruct ...


/** in mydata.c */
typedef union {
    mystruct public_block;
    struct mystruct_data_s {
        int64_t d1;
        int16_t d2
        int16_t d3;
    } data;
} mystruct_data;

// Optionally use '==' instead of '<=' to force minimal space usage
_Static_assert (sizeof(struct mystruct_data_s) <= sizeof(mystruct), "mystruct not big enough");

mystruct createMystruct(){
    static mystruct_data mystruct_blank = { .data = { .d1 = 1, .d2 = 2, .d3 = 3 } };
    return mystruct_blank.public_block;
}

int16_t exampleMystructGetter(mystruct *p) {
    mystruct_data *a = (mystruct_data*)p;
    return a->data.d2;
}

在gcc 4.7.3下,这个编译没有警告。通过getter创建和访问的简单测试程序也可以按预期编译和工作。