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(由第三方编写)field_3field_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_struct调用之前转换指针是否安全ext_function(如下所示)?

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

4 回答 4

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;
}

更干净,除非您每秒执行数千次,否则性能损失应该很小。

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

于 2012-12-03T15:04:49.403 回答
4

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

示例 2 在声明之后

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

typet1和指向的类型tp1是兼容的。Typet1也与 type 兼容struct s1,但与类型struct s2、、 或t2所指向的类型不兼容。tp2int

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

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

一种更简洁的方法是struct完全隐藏你的,用一个不起眼的句柄(typedef顶部的一个void*)替换它,并提供用于操作struct. 这样你就可以完全控制你的结构struct:你可以随意重命名它的字段,尽可能多地改变布局,改变字段的底层类型,以及做你通常做的其他事情避免当struct您的客户知道内部布局时。

于 2012-12-03T15:14:35.280 回答
2

我认为这不是一个好主意,因为很难跟踪结构是否已转换(尤其是在代码很大的情况下)。也将它转换为 const 并不能保证它不会被转换为non-const structure以后。

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

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

于 2012-12-03T16:04:06.630 回答
-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
$ 
于 2012-12-03T15:10:12.650 回答