我在C语言中拥有以下内容(不是C ++!):
module.c
struct Private {...};
void foo(void* private, int param) {...}
module.h
#define PRIVATE_SIZE ???;
void foo(void* private, int param);
main.c
char m1[PRIVATE_SIZE];
char m2[PRIVATE_SIZE];
int main()
{
foo(m1, 10);
foo(m2, 20);
}
如何在编译时公开sizeof(Private),以便应用程序可以 静态 分配其存储空间,而不公开私有类型?
请注意,这是一个非常有限的嵌入式系统,因此无法进行动态分配。
答案 0 :(得分:4)
您不应向调用者公开该结构的大小,因为这破坏了首先具有私有封装的整个目的。分配您的私人数据与呼叫者无关。另外,请避免使用void*
,因为它们完全缺乏类型安全性。
这是您在C中编写私有封装的方式:
typedef struct module module;
。呼叫者代码可能类似于:
#include "module.h"
...
module* m;
result = module_init(&m)
module_init
函数充当“构造函数”,在module.h中声明并在module.c中定义:
bool module_init (module** obj)
{
module* m = malloc(sizeof *m);
...
m->something = ...; // init private variables if applicable
*obj = m;
return true;
}
如果调用者确实需要知道对象的大小,则仅用于硬拷贝等目的。如果有需要,请提供一个复制函数,该函数封装分配和复制(“复制构造函数”),例如:
result module_copy (module** dst, const module* src);
编辑:
请注意,分配方式是一个单独的问题。您不必在上述设计中使用动态分配。例如,在嵌入式系统中,通常使用静态内存池代替。参见Static allocation of opaque data types
答案 1 :(得分:1)
在一致的C代码中,即使您在编译时知道其大小,也无法创建任意未知类型的静态实例(即使您知道对齐方式也是如此)。
假设您还是尝试这样做。给定宏或枚举PRIVATE_SIZE
的大小,您将如何做?
unsigned char obj[PRIVATE_SIZE];
然后您将(void*)obj
传递到需要的地方,对吗?
好吧,这违反了别名规则。尽管您可以合法地访问任何对象中的任何单个字符/字节,但您不能以相反的方式说这些字符不是字符,而是仅存储在其他类型之后。也就是说,在法律上,您不能通过聪明的裤子类型(例如short int
)将obj[2]
和obj[3]
叠加在((struct Private*)obj)->my_short = 2;
和memcpy()
之上。进行此类操作的唯一合法方法是通过memcpy(&temp, obj, sizeof temp);
,例如obj[]
,然后在修改后返回。或者,您需要使用unsigned char obj[PRIVATE_SIZE]
的单个字符。
有两种可能的方式来做到这一点。在另一个答案中描述了一个,基本上定义了已知类型的实例,但只让外界拥有指向它的指针。
另一个非常相似,在汇编代码中对其进行定义,然后再次让外部世界找到它的指针。组装方式的“美”在于,您实际上只需要一个名称,一个对齐方式和一个大小即可为命名对象分配空间。
如果将实例放在特殊的数据段中(请参阅gcc的section属性和链接脚本),您甚至可以将所有实例放在同一位置(认为是数组),甚至可以找到它们的累积大小因此计数。
在不显着违反任何C规则的情况下,另一件事是仍然使用此// struct Private* launder(unsigned char*);
.text
.globl launder
launder:
move %first_param_reg, %return_reg
ret
技巧,但是通过将其不变地传递给C编译器无法查看的汇编函数来清洗它,例如像
unsigned char obj[PRIVATE_SIZE]
但是您确实需要将double obj[PRIVATE_SIZE / sizeof(double)]
更改为与您的架构具有适当一致性的内容,例如long long
(如果您更喜欢这种方式,则与PRIVATE_SIZE
相同)。
对于#include "mod.h" // mod.h defines PRIVATE_SIZE
struct Private { ... };
extern char StAtIcAsSeRt[sizeof(struct Private) == PRIVATE_SIZE];
,您可以在编译时检查它是否与类型的大小匹配,例如
<div class="modal fade" id="myModal">
<div class="modal-dialog">
<div class="modal-content bmd-modalContent">
<div class="modal-body">
<div class="close-button">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
</div>
<div class="embed-responsive embed-responsive-16by9">
<iframe id="formIframe" class="embed-responsive-item" frameborder="0"></iframe>
</div>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
答案 2 :(得分:1)
您不能为诸如此类的结构分配大小,因为在编译时尚不知道大小。即使您确实知道运行时的大小,但由于对齐问题,仍然会有问题。
有一个可能的解决方案,涉及定义一个与私有结构具有相同大小和对齐要求的单独结构。
例如:
module.h:
#include <inttypes.h>
struct Public {
uint64_t opaque1;
uint64_t opaque2;
uint64_t opaque3;
};
void init(struct Public *p);
module.c:
#include <assert.h>
#include <stdalign.h>
#include "module.h"
struct Private {
int a;
double b;
float c;
};
static_assert(sizeof(struct Private)==sizeof(struct Public), "sizes differ");
static_assert(alignof(struct Private)==alignof(struct Public), "alignments differ");
void init(struct Public *p)
{
struct Private *pr = (struct Private *)p;
pr->a = 2;
pr->b = 2.5;
pr->c = 2.4f;
}
保证Public
和Private
结构的大小相同,并且对齐方式应相同。用户可能会编写公共结构的“不透明”字段,在这种情况下,您可能会遇到有关有效类型的别名问题,但是如果可以信任用户,则应该可以。
另一个更可靠的选择是,如果您对要支持的最大对象数有所了解。如果是这种情况,您可以在实现文件中包含这些对象的静态数组,并且init函数将返回指向此列表中的对象之一的指针。然后,您将拥有一个相关的清理功能,该功能可以释放实例。
例如:
module.c:
struct Private {
int a;
double b;
float c;
};
struct PrivateAllocator {
struct Private obj;
int used;
};
struct PrivateAllocator list[5] = {
{ { 0, 0, 0}, 0 },
{ { 0, 0, 0}, 0 },
{ { 0, 0, 0}, 0 },
{ { 0, 0, 0}, 0 },
{ { 0, 0, 0}, 0 }
};
struct Private *private_init()
{
int i;
for (i=0; i<5; i++) {
if (!list[i].used) {
list[i].used = 1;
return &list[i].obj;
}
}
return NULL;
}
void private_free(struct Private *p)
{
int i;
for (i=0; i<5; i++) {
if (&list[i].obj == p) {
list[i].used = 0;
return;
}
}
}
答案 3 :(得分:0)
如何在不公开C结构大小的情况下公开其C结构大小?
如果可以妥协一些:(静态-> main()
本地)
对于可变长度数组(C99),请使用辅助函数并将该数组放入main()
。
module.h
size_t foo_size(void);
main.c
int main() {
char m1[foo_size()];
foo(m1, 10);
}
解决对齐问题所需的其他工作。
考虑放松您的目标,成为suggested。
答案 4 :(得分:0)
C99允许您使用可变长度数组。
private.h:
#include <stdio.h>
extern const size_t size;
private.c:
#include "private.h"
struct Private {
int x;
int y;
int z;
};
const size_t size = sizeof(struct Private);
main.c:
#include <stdio.h>
#include "private.h"
int main(void) {
char m1[size]; //variable length array
printf("Size of m1 = %ld\n", sizeof(m1));
}