簡體   English   中英

c中強制轉換結構的分段錯誤

[英]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;
}

您的代碼中至少有三個獨立的問題。

  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 ),並且您想要編寫對所有結構都統一工作的代碼。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM