c中铸造结构中的分割错误



为了封装结构成员(以与本问题中讨论的类似方式(,我创建了下面的代码。

在下面的代码中,我有一个 c-struct,它包含访问隐藏结构成员的方法(通过强制转换为结构,否则相同但没有隐藏属性(

#include <stdio.h>
typedef struct class {
int publicValue;
int (*getPV)();
void (*setPV)(int newPV);
} class;
typedef struct classSource {
int publicValue;
int apv;
int (*getPV)();
void (*setPV)(int newPV);
int PV;
} classSource;
class class_init() {
classSource cs;
cs.publicValue = 15;
cs.PV = 8;
int class_getPV() {
return cs.PV;
};
void class_setPV(int x) {
cs.PV = x;
};
cs.getPV = class_getPV;
cs.setPV = class_setPV;
class *c = (class*)(&cs);
return *c;
}

int main(int argc, const char * argv[]) {
class c = class_init();
c.setPV(3452);
printf("%d", c.publicValue);
printf("%d", c.getPV());
return 0;
}

当我运行它时,我收到分段错误错误。但是,我注意到,如果我注释掉某些代码行,它(似乎(工作正常:

#include <stdio.h>
typedef struct class {
int publicValue;
int (*getPV)();
void (*setPV)(int newPV);
} class;
typedef struct classSource {
int publicValue;
int apv;
int (*getPV)();
void (*setPV)(int newPV);
int PV;
} classSource;
class class_init() {
classSource cs;
cs.publicValue = 15;
cs.PV = 8;
int class_getPV() {
return cs.PV;
};
void class_setPV(int x) {
cs.PV = x;
};
cs.getPV = class_getPV;
cs.setPV = class_setPV;
class *c = (class*)(&cs);
return *c;
}

int main(int argc, const char * argv[]) {
class c = class_init();
c.setPV(3452);
//printf("%d", c.publicValue);
printf("%d", c.getPV());
return 0;
}

我认为这可能与使用初始值设定项将 getter 和 setter 方法添加到结构中有关,因为它们可能会覆盖内存。

我正在做的事情是未定义的行为吗?有没有办法解决这个问题?


编辑:在下面的答案的帮助下,我重写了代码。如果有人想看到实现,下面是修改后的代码

#include <stdio.h>
#include <stdlib.h>
typedef struct {
int pub;
} class;
typedef struct {
class public;
int PV;
} classSource;
int class_getPV(class *c) {
return ((classSource*)c)->PV;
}
void class_setPV(class *c, int newPV) {
((classSource*)c)->PV = newPV;
}
class *class_init() {
classSource *cs = malloc(sizeof(*cs));
if((void*)cs == (void*)NULL) {
printf("Error: malloc failed to allocate memory");
exit(1);
}
cs->public.pub = 10;
cs->PV = 8;
return &(cs->public);
}
int main() {
class *c = class_init();
class_setPV(c,4524);
printf("%dn",class_getPV(c));
printf("%dn",c->pub);
free(c);
return 0;
}

代码中至少有三个单独的问题。

  1. 您实际上没有">结构,否则相同但没有隐藏属性"。您的classclassSource结构在不同位置有其getPVsetPV成员。内部成员访问归结为结构开头的字节偏移量。为了获得工作的机会,您的代码需要在两种结构类型之间有一个共同的成员初始前缀(即去掉int apv;或将其移动到末尾(。

  2. 您按值返回结构,它会自动创建副本。您已经重新实现了对象切片问题:由于返回值的类型为class,因此只会复制class的成员。classSource的额外成员已被"切掉"。

  3. 您使用的是嵌套函数。这不是 C 的标准功能;GCC将其实现为扩展,并说:

    如果在包含函数退出后尝试通过其地址调用嵌套函数,则所有地狱都会松动。

    这正是代码中发生的情况:您正在调用c.setPV(3452);并在class_init返回后c.getPV

如果要解决这些问题,则必须:

  1. 修复结构定义。至少class的所有成员都需要以相同的顺序出现在classSource的开头。即使你这样做了,我不确定你是否仍然会遇到未定义的行为(例如,你可能违反了别名规则(。

    但是,我有点确定将一个结构嵌入另一个结构是可以的:

    typedef struct classSource {
    class public;
    int PV;
    } classSource;
    

    现在,您可以从初始值设定项返回&cs->public,并且方法可以将class *指针强制转换为classSource *。(我认为这是可以的,因为所有结构指针都具有相同的大小/表示形式,并且X.public作为第一个成员保证具有与X相同的内存地址。

  2. 将代码更改为改用指针。返回指向结构的指针可以避免切片问题,但现在您必须负责内存管理(malloc结构并注意稍后free它(。

  3. 不要使用嵌套函数。而是将指向对象的指针传递给每个方法:

    class *c = class_init();
    c->setPV(c, 3452);
    int x = c->getPV(c);
    

    这有点乏味,但这就是例如C++在引擎盖下所做的,本质上。除了C++没有将函数指针放在对象本身中;没有理由什么时候可以使用正常功能:

    setPV(c, 3452);
    int x = getPV(c);
    

    。或者使用单独的(全局、常量、单例(结构,该结构仅存储指向方法的指针(而不存储数据(。然后,每个对象仅包含指向此方法结构的指针(这称为 vtable(:

    struct classInterface {
    void (*setPV)(class *, int);
    int (*getPV)(const class *);
    };
    static const classInterface classSourceVtable = {
    class_setPV,  // these are normal functions, defined elsewhere
    class_getPV
    };
    

    方法调用如下所示:

    c->vtable->setPV(c, 1234);
    int x = c->vtable->getPV(c);
    

    但是,如果您有几种不同的结构类型共享一个公共接口(class(,并且您希望编写在所有结构类型上统一工作的代码,则这主要很有用。

相关内容

  • 没有找到相关文章

最新更新