代码之家  ›  专栏  ›  技术社区  ›  anon

c语言中cast结构的分段错误

  •  0
  • anon  · 技术社区  · 6 年前

    尝试封装结构成员(以类似的方式 this 问题),我创建了下面的代码。

    在下面的代码中,我有一个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("%d\n",class_getPV(c));
        printf("%d\n",c->pub);
    
        free(c);
        return 0;
    }
    
    1 回复  |  直到 6 年前
        1
  •  1
  •   melpomene    6 年前

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

    1. 你实际上没有 结构相同,但没有隐藏属性 “。你的 class classSource 结构有他们的 getPV setPV 不同地方的成员。内部成员访问可归结为从结构开始的字节偏移量。要想让代码工作起来,您的代码需要在两种结构类型之间有一个通用的成员前缀(即去掉 int apv; 或者把它移到最后)。

    2. 您将按值返回一个struct,它将自动生成一个副本。你已经重新实现了 object slicing 问题:因为返回值具有 ,只有 将被复制。的额外成员 类源 已经被“砍掉了”。

    3. 您正在使用嵌套函数。这不是c的标准特性; GCC implements it as an extension 并说:

      如果试图在包含函数退出后通过其地址调用嵌套函数,则所有的hell都将崩溃。

      这正是你代码中发生的事情:你在打电话 c.setPV(3452); c.getPV 之后 class_init 已经回来了。

    如果你想解决这些问题,你必须:

    1. 修复结构定义。至少所有成员 需要出现在 类源 同样的顺序。即使你这样做,我也不确定你是否还会遇到未定义的行为(例如,你可能违反了别名规则)。

      不过,我有点确信,在另一个结构中嵌入一个结构是可以的:

      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);
      

      …或者使用一个单独的(global,constant,singleton)结构,它只存储指向方法(而不存储数据)的指针。然后,每个对象只包含指向此方法结构(这称为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);
      

      但是,如果有几个不同的结构类型共享公共接口,则这一点非常有用( )你想写的代码能统一地在它们上面工作。