【发布时间】:2013-07-12 15:34:14
【问题描述】:
我想创建一个不透明类型,同时仍然允许该类型的用户实例化它,例如通过键入在堆栈上
struct Foo obj;
/*...*/
Foo_init(&obj);
Foo_do_something(&obj, param);
/*...*/
惯用的不透明指针方法不允许此示例的第一行。 作为一种解决方法,我在公共头文件中放置了一个公共但不透明的“数据”数组 具有固定大小。这似乎适用于一些例子,但我有点不确定两点:
在 Foo_get_bar 和 Foo_set_bar 中转换数据数组的地址是否安全?这在我的测试中运行良好,但看起来有问题。
如果 FOO_DATA_SIZE 保持不变,期望用户代码中的 ABI 兼容性是否合理?
main.c(示例用户代码)
#include <stdio.h>
#include <limits.h>
#include "foo.h"
int main() {
struct Foo foo;
Foo_init(&foo);
Foo_set_bar(&foo, INT_MAX);
int bar = Foo_get_bar(&foo);
printf("Got bar: %d\n", bar);
}
foo.h(公共标头)
#pragma once
#include <inttypes.h>
#define FOO_DATA_SIZE (64)
struct Foo {
uint8_t data[FOO_DATA_SIZE];
};
void Foo_init(struct Foo *f);
void Foo_set_bar(struct Foo *f, int barval);
int Foo_get_bar(struct Foo *f);
foo.c(实现)
#include "foo.h"
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
typedef int64_t bar_t;
struct Foo_private {
bar_t bar;
};
_Static_assert(sizeof(struct Foo_private) <= FOO_DATA_SIZE,
"FOO_DATA_SIZE is insufficient for struct Foo_private");
void Foo_init(struct Foo *foo) {
struct Foo_private foodata;
foodata.bar = (bar_t)0;
memcpy(foo->data, &foodata, sizeof(struct Foo_private));
}
void Foo_set_bar(struct Foo *foo, int barval) {
struct Foo_private *foodata = (void*)&(foo->data);
foodata->bar = (bar_t)barval;
int stored = (int)foodata->bar;
if (stored != barval) {
fprintf(stderr, "Foo_set_bar(%"PRId64"): warning: bar rounded to %"PRId64"\n",
(int64_t)barval, (int64_t)stored);
}
}
int Foo_get_bar(struct Foo *foo) {
struct Foo_private *foodata = (void*)&(foo->data);
bar_t bar = foodata->bar;
return (int)bar;
}
【问题讨论】:
-
这是荒谬的。根据定义,如果它是不透明的,编译器不知道它有多大,所以它不能分配它。
-
几天前有人问过这个问题。不,这是不安全的,因为
int8_t和int64_t的对齐要求不同。另外,你弄错了大小 - 它应该是8而不是64。 -
@Kevin 我想你误解了这个问题。那不是OP要问的,请阅读第二段的第一句话。
-
@H2CO3 - 感谢您的对齐评论。我对 64 的想法不是猜测实现的大小,而是尝试猜测实现对我的结构所需的上限。
-
足够大的
maxalign_t数组应该用作存储。
标签: c