Segmentation fault in cast struct in c
为了封装结构成员(以与本问题讨论类似的方式),我创建了以下代码。
在下面的代码中,我有一个c结构,它包含一些方法来访问隐藏的结构成员(通过转换为结构,否则将其转换为其他结构,但没有隐藏的属性)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | #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; } |
运行此命令时,出现分段错误错误。但是,我注意到,如果我注释掉某些代码行,它似乎可以正常工作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | #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方法添加到结构中有关,因为这些方法可能会覆盖内存。
我正在做未定义的行为吗?有没有办法解决这个问题?
编辑:在以下答案的帮助下,我重新编写了代码。如果有人想看一下实现,下面是修改后的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | #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\ ",class_getPV(c)); printf("%d\ ",c->pub); free(c); return 0; } |
您的代码中至少存在三个独立的问题。
您实际上没有"结构,否则没有隐藏的属性"。您的
您正在按值返回结构,该结构会自动创建一个副本。您已经重新实现了对象切片问题:由于返回值的类型为
您正在使用嵌套函数。这不是C的标准功能。 GCC将其实现为扩展,并说:
If you try to call the nested function through its address after the containing function exits, all hell breaks loose.
这正是代码中发生的事情:
如果要解决这些问题,则必须:
修复您的结构定义。至少
我可以肯定将一个结构嵌入另一个结构中是可以的,但是:
1 2 3 4 | typedef struct classSource { class public; int PV; } classSource; |
现在,您可以从初始化程序中返回
更改代码以改为使用指针。返回指向结构的指针可以避免切片问题,但是现在您必须注意内存管理(
不要使用嵌套函数。而是将指向该对象的指针传递给每个方法:
1 2 3 | class *c = class_init(); c->setPV(c, 3452); int x = c->getPV(c); |
这有点乏味,但这就是例如C本质上是在后台执行的。除了C不会将函数指针放在对象本身之外;没有理由何时可以使用常规功能:
1 2 | setPV(c, 3452); int x = getPV(c); |
...或使用单独的(全局,常量,单例)结构仅存储指向方法的指针(不包含数据)。然后,每个对象仅包含一个指向此方法结构的指针(称为vtable):
1 2 3 4 5 6 7 8 | struct classInterface { void (*setPV)(class *, int); int (*getPV)(const class *); }; static const classInterface classSourceVtable = { class_setPV, // these are normal functions, defined elsewhere class_getPV }; |
方法调用如下所示:
1 2 | c->vtable->setPV(c, 1234); int x = c->vtable->getPV(c); |
但是,如果您有几种不同的结构类型共享一个公共的公共接口(