[英]flexible array in C and dereferencing type-punned pointer error
When I try to compile the code below with gcc -O3 -Wall -Werror -std=c99 main.c
I get an error like "dereferencing type-punned pointer will break strict-aliasing rules" at #3, but not in #2 or #1. 当我尝试使用
gcc -O3 -Wall -Werror -std=c99 main.c
编译下面的代码时,我得到一个错误,如“取消引用类型 - 惩罚指针将破坏严格别名规则” #3,但不会在#2中或#1。 I can dereference type-punned "char *", but why can't I do the same with flexible arrays? 我可以取消引用类型惩罚“char *”,但为什么我不能对灵活的数组做同样的事情呢?
#include <stdlib.h>
#include <stdio.h>
struct Base {
void (*release) (struct Base *);
size_t sz;
char *str_foo;
char rest[];
};
struct Concrete {
char *str_bar;
};
void
Base_release(struct Base *base)
{
free(base);
}
struct Base *
Base_new(size_t sz_extra)
{
size_t sz = sizeof(struct Base) + sz_extra;
struct Base *base = (struct Base *)malloc(sz);
base->release = &Base_release;
base->sz = sz;
base->str_foo = "foo";
return base;
}
#define BASE_FREE(_obj) (_obj)->release(_obj)
#define BASE_CAST(_type, _obj) ((struct _type *)((_obj)->rest))
#define BASE_CAST_2(_type, _obj) ((struct _type *)((char *)(_obj)+sizeof(struct Base)))
struct Base *
Concrete_new()
{
struct Base *base = Base_new(sizeof(struct Concrete));
struct Concrete *concrete = BASE_CAST(Concrete, base);
concrete->str_bar = "bar";
return base;
}
int main(int argc, const char *argv[])
{
struct Base *instance = Concrete_new();
printf("Base str: %s\n", instance->str_foo);
// #1 - Legal
struct Concrete *cinstance = BASE_CAST(Concrete, instance);
printf("#1: Concrete str: %s\n", cinstance->str_bar);
// #2 - Legal
printf("#2: Concrete str: %s\n", BASE_CAST_2(Concrete, instance)->str_bar);
// #3 - Compile error
printf("#3: Concrete str: %s\n", BASE_CAST(Concrete, instance)->str_bar);
BASE_FREE(instance);
return 0;
}
EDIT 1: There is more concrete example showing problem below: 编辑1:下面有更具体的例子显示问题:
struct s {
char a;
};
char *const a = malloc(sizeof(struct s));
char b[sizeof(struct s)];
((struct s *)((char *)a))->a = 5; // This is a valid case
((struct s *)(a))->a = 5; // OK
((struct s *)((char *)b))->a = 5; // ???
First of all, it isn't related with flexible arrays but rather with any array. 首先,它与灵活数组无关,而与任何数组无关。 You could use fixed-size-big-enough array and see the same results.
您可以使用固定大小足够大的数组并查看相同的结果。
Most obvious workaround is your BASE_CAST_2
. 最明显的解决方法是您的
BASE_CAST_2
。 Another one could be using offsetof
instead of sizeof
, especially in cases where structures tail is not flexible. 另一个可能是使用
offsetof
而不是sizeof
,特别是在结构尾部不灵活的情况下。
Difference between first and third cases is tricky. 第一和第三种情况之间的差异很棘手。 They're both breaking strict aliasing rules, but gcc sometimes allows that, when it can't determine origin of lvalue.
它们都破坏了严格的别名规则,但是当它无法确定左值的来源时,gcc有时会允许这样做。 However, with
-Wstrict-alising=2
it will give warning in both cases. 但是,使用
-Wstrict-alising=2
它会在两种情况下发出警告。 I'm not sure it is guaranteed to generate valid code even if no warning issued with standard -Wstrict-aliasing
(but in that given example it does). 我不确定它是否能保证生成有效的代码,即使没有使用标准
-Wstrict-aliasing
发出警告(但在那个例子中它也是如此)。
Second case looks ok because casting structure*
to char*
is allowed. 第二种情况看起来很好,因为允许将
structure*
为char*
。 There is however a difference between char*
and char[]
types. 但是
char*
和char[]
类型之间存在差异。
They all break strict aliasing by aliasing an array of char as a type that is not char. 它们都通过将char数组别名作为非char类型来破坏严格别名。 (The reverse of that is permitted though).
(尽管如此,允许相反)。
Also they all may have alignment problems; 他们都可能有对齐问题;
rest
might not be correctly aligned for a struct. 可能没有为结构正确对齐
rest
。
You might not get a warning because: 您可能不会收到警告,因为:
If you replaced rest
with a pointer to dynamically allocated memory then both of those problems go away, since the "effective type" of dynamically allocated memory is determined by what you store in it. 如果用指向动态分配内存的指针替换
rest
,那么这两个问题都会消失,因为动态分配内存的“有效类型”取决于你在其中存储的内容。
Note, it seems to me a much better solution to do: 请注意,在我看来,这是一个更好的解决方案:
struct Concrete
{
struct Base base;
char const *str_bar;
};
or alternatively, keep Concrete as you have it and do: 或者,保持混凝土,你可以做:
struct BaseConcrete
{
struct Base base;
struct Concrete concrete;
};
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.