在C语言中如何在结构体指针之间转换

15
请考虑以下代码。
typedef struct{
    int field_1;
    int field_2;
    int field_3;
    int field_4;

    uint8_t* data;
    uint32_t data_size;
} my_struct;

void ext_function(inalterable_my_struct* ims, ...);

我希望允许第三方编写的ext_function仅修改my_struct中的field_3field_4。所以我做了以下操作:

typedef struct{
    const int field_1;
    const int field_2;
    int field_3;
    int field_4;

    const uint8_t* data;
    const uint32_t data_size;
} inalterable_my_struct;

void ext_function(inalterable_my_struct* ims, ...);

在调用 ext_function 之前,将指针在 my_structinalterable_my_struct 之间进行转换是否安全(如下所示)?

void call_ext_function(my_struct* ms){
    inalterable_my_struct* ims = (inalterable_my_struct*)ms;
    ext_function(ims, ...);
}

如果您不希望ext_function修改data,则应将其声明为const uint8_t * const data; - Henrik
4个回答

7

我不认为这是一个好主意。

被调用的函数总是可以取消任何const属性,并且在需要时修改数据。

如果您可以控制调用点,最好创建一个副本并使用指向副本的指针调用函数,然后将您关心的两个字段复制回去:

void call_ext_function(my_struct* ms)
{
    my_struct tmp = *ms;
    ext_function(&tmp, ...);
    ms->field_3 = tmp.field_3;
    ms->field_4 = tmp.field_4;
}

这样做会使代码更加简洁易读,并且除非您每秒执行此操作数千次,否则性能损失应该很小。

如果函数涉及指针数据,则可能还需要模拟指针数据。


4
根据C99标准,即使两个struct的声明完全相同,它们也不会具有兼容的类型。参见第6.7.7.5节:

例2 在以下声明之后

typedef struct s1 { int x; } t1, *tp1;
typedef struct s2 { int x; } t2, *tp2;

类型t1和指向类型tp1的类型是兼容的。类型t1也与类型struct s1兼容,但与类型struct s2t2,指向类型tp2的类型或int不兼容。
此外,具有不同限定符的两种类型不被认为是兼容的:
为了使两个限定类型兼容,它们都应具有兼容类型的相同限定版本;在说明符或限定符列表中,限定符的顺序不影响指定的类型。
一种更干净的方法是完全隐藏您的struct,用模糊的句柄(void*上的typedef)替换它,并提供用于操作struct元素的函数。这样,您将完全控制struct的结构:可以随意重命名其字段,随意更改布局,更改字段的基础类型,以及进行其他在客户端已知struct内部布局时通常避免的事情。

2
我认为这不是一个好主意,因为很难跟踪结构体是否已经转换(特别是如果代码很大)。将其转换为const也不能保证它不会被转换为非const结构体。 unwind提供的解决方案非常好。另一种(更明显的)解决方案是将结构体分成两个较小的部分。
typedef struct{
    const int field_1;
    const int field_2;
    const uint8_t* data;
    const uint32_t data_size;
} inalterable_my_struct;

typedef struct{
    int field_3;
    int field_4;
} my_struct;

void ext_function(const inalterable_my_struct* const ims, my_struct* ms ...);

我在上述调用中也将指针设为常量,但这并非必要。


-2

尽管标准没有说明,但它可能在大多数编译器上运行。如果你真的必须要做一些更便携的事情,甚至可以使用联合。但是它不会改变任何东西。

这就是为什么它不会改变任何东西的原因:

$ cat foo.c
struct foo {
    const int a;
    int b;
};

void
foo(struct foo *foo)
{
    foo->a = 1;
}
$ cc -c foo.c
foo.c: In function ‘foo’:
foo.c:9: error: assignment of read-only member ‘a’
$ cc -Dconst= -c foo.c
$ 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接