普通C多态,类型惩罚和严格别名.这有多合法?

Vla*_*nev 8 c polymorphism strict-aliasing language-lawyer type-punning

我一直试图弄清楚以下是合法的,我真的可以使用一些帮助.

#include <stdio.h>
#include <stdlib.h>

typedef struct foo {
    int foo;
    int bar;
} foo;

void make_foo(void * p)
{
    foo * this = (foo *)p;

    this->foo = 0;
    this->bar = 1;
}

typedef struct more_foo {
    int foo;
    int bar;
    int more;
} more_foo;

void make_more_foo(void * p)
{
    make_foo(p);

    more_foo * this = (more_foo *)p;
    this->more = 2;
}

int main(void)
{
    more_foo * mf = malloc(sizeof(more_foo));

    make_more_foo(mf);
    printf("%d %d %d\n", mf->foo, mf->bar, mf->more);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

据我所知,这样做就是打字,并且应该违反严格的别名规则.不过吗?传递的指针无效.您可以按照自己的意愿解释无效指针,对吗?

另外,我读到可能存在内存对齐问题.但结构对齐是确定性的.如果初始成员是相同的,那么它们将以相同的方式对齐,并且从more_foo指针访问所有foo成员应该没有问题.那是对的吗?

GCC在没有警告的情况下编译-Wall,程序按预期运行.但是,我不确定它是否是UB以及为什么.

我也看到了这个:

typedef union baz {
    struct foo f;
    struct more_foo mf;
} baz;

void some_func(void)
{
    baz b;
    more_foo * mf = &b.mf; // or more_foo * mf = (more_foo *)&b;

    make_more_foo(mf);
    printf("%d %d %d\n", mf->foo, mf->bar, mf->more);
}
Run Code Online (Sandbox Code Playgroud)

似乎是被允许的.由于联合的多态性,编译器可以使用它.那是对的吗?这是否意味着通过使用严格别名进行编译,您不必使用联合并且只能使用结构?

编辑:union baz现在编译.

pm1*_*101 0

我想说这并不严格,因为如果你改变“foo”结构,“more foo”结构也必须随之改变。“foo”必须成为“more foo”的基础,这是继承,而不是多态。但是您可以使用函数指针引入多态性来帮助处理这些结构。

例子

#include <stdio.h>
#include <stdlib.h>

#define NEW(x) (x*)malloc(sizeof(x));

typedef struct 
{
    void(*printme)(void*);

    int _foo;
    int bar;

} foo;

typedef struct 
{
    // inherits foo
    foo base;
    int more;
} more_foo;


void foo_print(void *t)
{
    foo *this = (foo*)t;

    printf("[foo]\r\n\tfoo=%d\r\n\tbar=%d\r\n[/foo]\r\n", this->bar, this->_foo);
}

void more_foo_print(void *t)
{
    more_foo *this = t;

    printf("[more foo]\r\n");

    foo_print(&this->base);

    printf("\tmore=%d\r\n", this->more);

    printf("[/more foo]\r\n");
}


void foo_construct( foo *this, int foo, int bar )
{
    this->_foo = foo;
    this->bar = bar;

    this->printme = foo_print;
}

void more_foo_construct(more_foo *t, int _foo, int bar, int more)
{
    foo_construct((foo*)t, _foo, bar);

    t->more = more;

    // Overrides printme
    t->base.printme = more_foo_print;
}

more_foo *new_more_foo(int _foo, int bar, int more)
{
    more_foo * new_mf = NEW(more_foo);

    more_foo_construct(new_mf, _foo, bar, more);

    return new_mf;
}

foo *new_foo(int _foo, int bar)
{
    foo *new_f = NEW(foo);

    foo_construct(new_f, _foo, bar);

    return new_f;
}

int main(void)
{
    foo * mf = (foo*)new_more_foo(1, 2, 3);
    foo * f = new_foo(7,8);

    mf->printme(mf);

    f->printme(f);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)
  • 创建“more foo”时 printme() 被覆盖。(多态性)

  • more_foo 包括 foo 作为基本结构(继承),因此当“foo”结构更改时,“more foo”随之更改(例如添加的新值)。

  • more_foo 可以转换为“foo”。