说我有以下结构:
struct StructA
{
int some_member;
double some_other_member;
};
struct StructB
{
char a_char;
char another_char;
};
现在说我在内存中有一些包含这些结构的缓冲区,我想解析它。缓冲区中的每个结构前面都有一个描述结构类型的int
。像这样:
struct Descriptor
{
int type;
union
{
struct StructA a;
struct StructB b;
} data;
};
我想做的是将指针转换为缓冲区(char *
)到struct Descriptor *
,读取类型,然后访问union的正确成员,然后按指针前进类型成员的大小+正确联合成员的大小(例如,sizeof(int) + sizeof(struct StructA)
)。
但是,这是有效的C代码吗? (从某种意义上说,它不会调用未定义的行为。)我主要担心的是内存缓冲区可能小于sizeof(struct Descriptor)
,因为它只包含StructB
和前面的类型字段。
当底层内存不足以容纳整个结构时,使用指向结构的指针是否合法,即使我只访问内存的有效部分? 编辑:如果它对结构无效,它对联盟有效吗?
答案 0 :(得分:4)
不,使用简单的sizeof来计算偏移量是不正确的,因为它没有考虑可能的填充。
您应该使用stddef.h中定义的offsetof宏,它将适用于任何填充:
struct Descriptor* s = /*member data.a is initialized*/;
const size_t aoff = offsetof( struct Descriptor , data.a );
const size_t doff = offsetof( struct StructA , some_other_member );
double* p = ( double* )( ( unsigned char* )s + aoff + doff );
p
现在指向some_other_member
成员data.a
中的成员s
。
答案 1 :(得分:2)
我认为只要你正确地取消引用指针就行它是有效的,即不要超出我假设你事先知道的缓冲区大小的数据并正确处理对齐。所以你只需要担心正确处理内存缓冲区,除此之外,这是标准对这种转换的说法
6.3.2.3指针
- 指向对象类型的指针可以转换为指向不同对象类型的指针。如果结果指针未正确对齐引用类型 68),则行为未定义。否则,当再次转换回来时,结果将等于原始指针。当指向对象的指针转换为指向字符类型的指针时,结果指向对象的最低寻址字节。结果的连续递增,直到对象的大小,产生指向对象剩余字节的指针。
醇>68) 一般来说,''正确对齐''这个概念是传递性的:如果指向类型A的指针正确对齐指向类型B的指针,这反过来又是正确的对齐指向类型C的指针,然后指向类型A的指针正确对齐指向类型C的指针。
然后,这就是关于作业的说法
6.5.16.1简单分配
<强>语义强>
- 如果从另一个以第一个对象的存储方式重叠的对象读取存储在对象中的值,则重叠应该是精确的,并且这两个对象应具有兼容类型的合格或不合格版本;否则,行为未定义。
醇>
因此,如果重叠是精确的,那么你就可以了,并且定义了行为。