在C中的struct指针之间转换

时间:2012-12-03 14:58:52

标签: c casting

请考虑以下代码。

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(由第三方编写)仅修改field_3中的field_4my_struct。所以我做了以下几点:

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, ...);

在调用my_struct之前在inalterable_my_structext_function之间投射指针是否安全(如下所示)?

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

5 个答案:

答案 0 :(得分:7)

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

被调用函数总是可以抛弃任何const:ness,并根据需要修改数据。

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

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;
}
更清洁,除非你每秒数千次这样做,否则性能损失应该很小。

如果函数触及它,你可能也必须伪造基于指针的数据。

答案 1 :(得分:4)

根据C99标准,即使两个struct声明相同,它们也不具有兼容类型。从6.7.7.5节:

  

示例2声明之后

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

键入t1tp1指向的类型是兼容的。类型t1也与类型struct s1兼容,但与类型struct s2t2tp2指向的类型或{{1}不兼容}}

此外,具有不同限定符的两种类型不被视为兼容:

  

要兼容两种合格类型,两者都应具有相同的合格版本   兼容类型;说明符或限定符列表中类型限定符的顺序   不会影响指定的类型。

更干净的方法是完全隐藏您的int,将其替换为隐藏的句柄(struct之上的typedef)并提供操作void*元素的函数{1}}。通过这种方式,您可以完全控制struct的结构:您可以随意重命名其字段,根据需要随意更改布局,更改字段的基础类型,以及执行此操作当您的客户知道struct的内部布局时,您通常会避免的其他事情。

答案 2 :(得分:2)

我认为这不是一个好主意,因为很难跟踪结构是否已被强制转换(特别是如果代码很大)。同时将其强制转换为const并不能保证以后不会将其强制转换为non-const structure

展开提供的解决方案非常好。另一种(也是更明显的)解决方案是将结构分成两个较小的部分。

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 ...);

我在上面的调用中使指针也保持不变,但这不是必需的。

答案 3 :(得分:-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
$ 

答案 4 :(得分:-2)

写入曾经是const的成员可能不安全。它们可能已被编译器/链接器和操作系统放入只读存储器中。请参阅 How is read-only memory implemented in C?