[英]Segmentation fault in cast struct in c
为了封装结构成员(以与本问题讨论类似的方式),我创建了以下代码。
在下面的代码中,我有一个c结构,它包含一些方法来访问隐藏的结构成员(通过强制转换为结构,其他方法相同,但没有隐藏的属性)
#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("%d\n",class_getPV(c));
printf("%d\n",c->pub);
free(c);
return 0;
}
您的代码中至少有三个独立的问题。
实际上,您实际上没有“ 结构相同,但没有隐藏属性 ”。 您的class
和classSource
结构在不同位置具有其getPV
和setPV
成员。 内部成员访问可归结为从结构开头的字节偏移量。 为了获得成功的机会,您的代码将需要在两种结构类型之间具有相同的成员初始前缀(即,删除int apv;
或将其移至末尾)。
您将按值返回结构,该结构会自动创建一个副本。 您已经重新实现了对象切片问题:因为返回值的类型为class
,所以只会复制class
的成员。 classSource
的额外成员已被“分割”。
您正在使用嵌套函数。 这不是C的标准功能。 GCC将其实现为扩展,并说:
如果您尝试在包含函数退出后通过其地址调用嵌套函数,那么所有地狱都会变得松散。
这正是代码中发生的事情:您正在调用c.setPV(3452);
和class_init
返回后的c.getPV
。
如果要解决这些问题,则必须:
修复您的结构定义。 至少所有class
成员都必须以相同顺序出现在classSource
的开头。 即使您这样做,我也不确定您是否仍不会遇到未定义的行为(例如,您可能违反了别名规则)。
我可以肯定地将一个结构嵌入另一个结构中,但是:
typedef struct classSource { class public; int PV; } classSource;
现在,您可以从初始化程序中返回&cs->public
,并且您的方法可以将class *
指针转换回classSource *
。 (我认为这是可以的,因为所有结构指针都具有相同的大小/表示形式,并且保证X.public
作为第一个成员具有与X
相同的内存地址。)
更改代码以改为使用指针。 返回指向结构的指针可以避免切片问题,但是现在您必须注意内存管理( malloc
结构并注意以后free
它)。
不要使用嵌套函数。 而是将指向对象的指针传递给每个方法:
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
),并且您想要编写对所有结构都统一工作的代码。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.