简体   繁体   English

C中的灵活数组和解除引用类型 - 惩罚指针错误

[英]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: 您可能不会收到警告,因为:

  • The compiler's detection of aliasing violation is not that good, and/or 编译器检测到别名冲突并不是那么好,和/或
  • Your system actually permits this aliasing even though it is non-portable 您的系统实际上允许这种别名,即使它是不可移植的

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.

相关问题 gcc4.7错误 - 解除引用类型惩罚指针 - gcc4.7 error - dereferencing type-punned pointer 解除引用类型惩罚指针将破坏严格别名规则 - dereferencing type-punned pointer will break strict-aliasing rules 修复取消引用类型双关指针将破坏严格混叠 - Fix for dereferencing type-punned pointer will break strict-aliasing 取消引用类型惩罚指针将破坏严格别名规则 - Dereferencing type-punned pointer will break strict-aliasing rules 编译mpeg4ip会给出解引用类型标记的指针错误 - Compiling mpeg4ip gives dereferencing type-punned pointer error 解引用类型双关指针将打破严格的别名规则,而编译不能修复编译 C 代码 - Dereferencing type-punned pointer will break strict-aliasing rules while compiling cannot be fixed to compile C code 类型解引用类型化指针将破坏严格混叠规则 - Type dereferencing type-punned pointer will break strict-aliasing rule 调用free()包装器:解除引用类型惩罚指针将破坏严格别名规则 - Calling a free() wrapper: dereferencing type-punned pointer will break strict-aliasing rules 如何解决警告:解除引用类型惩罚指针将破坏严格别名规则 - How to resolve warning: dereferencing type-punned pointer will break strict-aliasing rules 警告“取消引用类型 - 惩罚指针将破坏严格别名规则”的结果 - Consequenes of warning “dereferencing type-punned pointer will break strict-aliasing rules”
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM