【发布时间】:2022-01-09 12:54:03
【问题描述】:
在 C 语言中,我想在结构的最末端放置一个char id,以便我可以从指向末端的指针中辨别结构类型结构的(动态分配的)。显然,最后填充的可能性使这变得困难。我想到了两种方法。
第一种方法是放置一个一直延伸到结构末尾的字符数组,以便(char*)ptr_to_end - 1 始终指向有效字符。我认为如果编译器没有做任何有趣的事情,这应该可以工作。否则,它应该无法编译:
typedef struct
{
int foo;
int bar;
char type;
} MyStructDummy;
typedef struct
{
int foo;
int bar;
char type[ sizeof( MyStructDummy ) - offsetof( MyStructDummy, type ) ];
} MyStruct;
_Static_assert(
sizeof( MyStruct ) == sizeof( MyStructDummy ),
"Could not ensure char at end of MyStruct"
);
第二种方法是使用offsetof 来始终将 malloc-ed bloc 作为单个(成员)变量访问,而不是作为一个完整的结构访问。这样,我们就避免了将结构的类型作为有效类型传递给整个块或意外更改填充值:
typedef struct
{
int foo;
int bar;
char type;
} MyStruct;
int *MyStruct_foo( void *end_ptr )
{
return (int*)( (char*)end_ptr - sizeof( MyStruct ) + offsetof( MyStruct, foo ) );
}
int *MyStruct_bar( void *end_ptr )
{
return (int*)( (char*)end_ptr - sizeof( MyStruct ) + offsetof( MyStruct, bar ) );
}
char *MyStruct_type( void *end_ptr )
{
return (char*)end_ptr - 1;
}
这两种方法中的任何一种都比另一种更可取吗?是否有现有的 C 习惯用法可以实现我想要实现的目标(我不能使用灵活的数组成员,因为我想保持 C++ 兼容性)?
谢谢!
编辑:
Karl 询问在结构的末尾放置一个 id 会有什么用处。考虑一下动态数组/向量的这种节省内存的实现:
//VecHdr is for vectors without an automatic element destructor function
//and whose capacity is < UINT_MAX
typedef struct
{
alignas( max_align_t )
unsigned int size;
unsigned int cap;
char type_and_flags; //At very end
} VecHdr; //Probable size: 16 bytes
//VecHdr is for vectors with an element destructor or whose capacity is >= UINT_MAX
typedef struct
{
alignas( max_align_t )
size_t size;
size_t cap;
void (*element_destructor_func)( void* );
char type_and_flags; //At very end
} VecHdrEx; //Probable size: 32 bytes
//...
int *foo = vec_create( int );
//This macro returns a pointer to a malloced block of ints, preceded by a VecHdr
int *bar = vec_create_ex( int, my_element_destructor );
//This macro returns a pointer to malloced block of ints, preceded by a VecHdrEx
vec_push( foo, 12345 );
//vec_push knows that foo is preceded by a VecHdr by checking (char*)foo - 1
//foo's VecHdr may eventually be replaced with a VecHdrEx if we add enough elements
vec_push( bar, 12345 );
//vec_push knows that bar is preceded by a VecHdrEx by checking (char*)foo - 1
【问题讨论】:
-
"我想在结构的最后放置一个
charid" - 为什么? “这样我就可以通过指向结构末尾的指针来识别结构类型” - 为什么要从末尾而不是从前面访问结构? -
@JDormer 我认为两种方法的内存使用量应该相同。该内存要么落在
char数组中,要么被填充消耗。 -
我仍然无法理解您试图用这种方法解决什么问题。请展示一个代码示例,否则您会因为没有关于指针的必要信息而遇到问题,并说明您为什么会出现这种情况。
-
我会在用户可见数组之前放置一个固定大小的主标题块,并在主标题块之前放置另一个带有附加数据的可选标题块。它的存在和大小由主块中的类型字段决定。
-
offsetof 似乎足够了。那时,将字符放在哪里都无关紧要。您也可以使用一个 int(或两个)来虚拟地保证对齐,尽管这不能保证
标签: c++ c language-lawyer padding