对象属性引用导致SIGSEGV

问题描述 投票:0回答:3
/******************************************************************************

Welcome to GDB Online.
GDB online is an online compiler and debugger tool for C, C++, Python, Java, PHP, Ruby, Perl,
C#, OCaml, VB, Swift, Pascal, Fortran, Haskell, Objective-C, Assembly, HTML, CSS, JS, SQLite, Prolog.
Code, Compile, Run and Debug online from anywhere in world.

*******************************************************************************/
#include <iostream>
#include <ctime>
#include <cstdlib>
#include <vector>
#include <cmath>
using namespace std;
class Okret {
public:
    string nazwa;
    int id, moc, szybkosc, zaloga, celnosc, hp;
    Okret(int identifier, string name, int power, int speed, int crew, int accuracy, int hitpoints) {
        nazwa = name;
        moc = power;
        szybkosc = speed;
        zaloga = crew;
        celnosc = accuracy;
        id = identifier;
        hp = hitpoints;
    }
    Okret() {}
    void get() {
        cout << "***************\nID: " << id << "\nNAZWA: " << nazwa << "\nMOC : " << moc << "\nSZYBKOSC: " << szybkosc << "\nHP: " << hp << "\nZALOGA: " << zaloga << "\nCELNOSC: " << celnosc << "\n\n";
    }
};
class Krazownik : public Okret {
public:
    int iloscDzial, zasieg;
    Krazownik(int identifier, string name, int power, int speed, int crew, int accuracy, int hitpoints, int cannonCount, int range) : Okret(identifier, name, power, speed, crew, accuracy, hitpoints) {
        iloscDzial = cannonCount;
        zasieg = range;
    }
    Krazownik() {}
    void get() {
        Okret::get();
        cout << "ILOSC DZIAL: " << iloscDzial << "\nZASIEG: " << zasieg << "\n\n";
    }
};
class LodzPodwodna : public Krazownik {
public:
    int glebokosc;
    bool czyAtom;
    LodzPodwodna(int identifier, string name, int power, int speed, int crew, int accuracy, int hitpoints, int cannonCount, int range, int depth, bool isAtomic) : Krazownik(identifier, name, power, speed, crew, accuracy, hitpoints, cannonCount, range) {
        glebokosc = depth;
        czyAtom = isAtomic;
    }
    LodzPodwodna() {}
    void get() {
        Krazownik::get();
        cout << "GLEBOKOSC: " << glebokosc << "\nATOM: " << czyAtom << "\n\n";
    }
};
class Flota {
public:
    Okret** okrety;
    Krazownik** krazowniki;
    LodzPodwodna** lpodwodne;
    int identifier = 0;
    int ships[3];
    Flota(int iloscOkretow, int iloscKrazownikow, int iloscLodziPodwodnych) {
        ships[0] = iloscOkretow;
        ships[1] = iloscKrazownikow;
        ships[2] = iloscLodziPodwodnych;
        okrety = new Okret * [ships[0]];
        krazowniki = new Krazownik * [ships[1]];
        lpodwodne = new LodzPodwodna * [ships[2]];
        string name[2][10] = { {"HMS", "LA", "OZM", "ZLB", "SHW", "CHM", "KTR", "RJ45", "FFK", "REP"}, {"Danietz", "Magda", "Patrycja", "MOSiR", "JTFolio", "Pandziak", "Morcinek", "Fentanyl", "Robercia", "Malpa"} }, typy[3] = { "Okrety:", "Krazowniki:", "Lodzie podw.:" };
        for (int i = 0; i < 3; i++)
            for (int j = 0; j < ships[i]; j++) {
                string tname = name[0][rand() % 10] + ' ' + name[1][rand() % 10];
                switch (i) {
                case 0:
                    okrety[j] = new Okret(identifier, tname, rand() % 11 + 15, rand() % 201 + 200, rand() % 4 + 1, rand() % 16 + 60, rand() % 100 + 300);
                    break;
                case 1:
                    krazowniki[j] = new Krazownik(identifier, tname, rand() % 21 + 40, rand() % 176 + 200, rand() % 21 + 10, rand() % 31 + 45, rand() % 500 + 750, rand() % 3 + 1, rand() % 76 + 75);
                    break;
                case 2:
                    lpodwodne[j] = new LodzPodwodna(identifier, tname, rand() % 11 + 35, rand() % 301 + 150, rand() % 3 + 2, rand() % 11 + 75, rand() % 250 + 600, 2, rand() % 101 + 150, rand() % 301 + 150, (rand() % 2 == 0) ? true : false);
                    break;
                }
                identifier++;
            }
        cout << "=== FLOTA ===\nOkretow: " << iloscOkretow << "\nKrazownikow: " << iloscKrazownikow << "\nLodzi podwodnych: " << iloscLodziPodwodnych << "\n=============\n";
        for (int i = 0; i < ships[0]; i++)
            okrety[i]->get();
        for (int i = 0; i < ships[1]; i++)
            krazowniki[i]->get();
        for (int i = 0; i < ships[2]; i++)
            lpodwodne[i]->get();
    }
};
bool checkVictory(int hp1, int hp2, string nazwa1, string nazwa2, int id1, int id2) {
    if (hp1 <= 0 && hp2 <= 0) {
        cout << "[REMIS] Oba '" << nazwa1 << "' oraz '" << nazwa2 << "' leza na dnie basenu! (OVERKILL: " << hp1 << " hp i " << hp2 << " hp)!\n";
        return true;
    }
    if (hp1 <= 0) {
        cout << "[WYGRANA] '" << nazwa2 << " " << id2 << "' zwyciezyl '" << nazwa1 << " " << id2 << "' (OVERKILL: " << hp1 << " hp)!\n";
        return true;
    }
    if (hp2 <= 0) {
        cout << "[WYGRANA] '" << nazwa1 << " " << id1 << "' zwyciezyl '" << nazwa2 << " " << id1 << "' (OVERKILL: " << hp2 << " hp)!\n";
        return true;
    }
    return false;
}
int rem(LodzPodwodna* statek1, LodzPodwodna* statek2) {
    LodzPodwodna* statek[2] = { statek1, statek2 };
    for (int i = 0; i < 2; i++) {
        if (rand() % 101 <= statek[0 + i]->celnosc) {
            statek[1 - i]->hp -= statek[0 + i]->moc * statek[0 + i]->iloscDzial - statek[1 - i]->glebokosc / 30;
            cout << "'" << statek[0 + i]->nazwa << " " << statek[0 + i]->id << "' godzi '" << statek[1 - i]->nazwa << "' (" << statek[0 + i]->moc * statek[0 + i]->iloscDzial - statek[1 - i]->glebokosc / 30 << " hp)! ";
            if (statek[1 - i]->hp > 0)
                cout << "Pozostalo " << statek[1 - i]->hp << " hp.";
            else if (statek[1 - i]->czyAtom) {
                cout << "\n[BOMBA] Awaryjna orpeda atomowa zostala wystrzelona! ";
                if (rand() % 101 < 13) {
                    statek[0 + i]->hp -= 9999;
                    cout << "Niezneutralizowana na czas, obraca cel w proch!";
                }
                else
                    cout << "Nie trafia!";
            }
            cout << "\n";
            if (checkVictory(statek[0]->hp, statek[1]->hp, statek[0]->nazwa, statek[1]->nazwa, statek[0]->id, statek[1]->id))
                return 0;
        }
        else
            cout << "'" << statek[0 + i]->nazwa << "' chybił!\n";
    }
    return 0;
}
void walka(Flota* flota1, Flota* flota2) {
    char dummy;
    while (1) {
        cout << "Oczekiwanie...\n>> ";
        cin >> dummy;
        Flota* args[2] = { flota1, flota2 };
        int total = flota1->ships[0] + flota1->ships[1] + flota1->ships[2], tura = 1;
        vector<vector<LodzPodwodna*>> floty(2, vector<LodzPodwodna*>(total));
        for (int i = 0; i < 2; i++)
            for (int j = 0; j < args[i]->ships[0]; j++) {
                Okret* okret = args[i]->okrety[j];
                floty[i][j] = new LodzPodwodna(okret->id, okret->nazwa, okret->moc, okret->celnosc, okret->zaloga, okret->celnosc, okret->hp, 1, 130, 1, false);
                floty[i][j]->get();
            }
        for (int i = 0; i < 2; i++)
            for (int j = 0; j < args[i]->ships[1]; j++) {
                Krazownik* krazownik = args[i]->krazowniki[j];
                floty[i][j] = new LodzPodwodna(krazownik->id, krazownik->nazwa, krazownik->moc, krazownik->celnosc, krazownik->zaloga, krazownik->celnosc, krazownik->hp, krazownik->iloscDzial, krazownik->zasieg, 1, false);
                floty[i][j]->get();
            }
        for (int i = 0; i < 2; i++)
            for (int j = 0; j < args[i]->ships[2]; j++)
                floty[i][j] = args[i]->lpodwodne[j];
        cout << "\n######################\n        WALKA\n######################\n";
        bool hasEnded = false;
        while (!hasEnded) {
            hasEnded = true;
            cout << "\n--- TURA " << tura;
            for (int i = 0; i < total; i++) {
                cout << "\n";
                if (checkVictory(floty[0][i]->hp, floty[1][i]->hp, floty[0][i]->nazwa, floty[1][i]->nazwa, floty[0][i]->id, floty[1][i]->id))
                    continue;
                if (floty[0][i]->szybkosc >= floty[1][i]->szybkosc)
                    rem(floty[0][i], floty[1][i]);
                else
                    rem(floty[1][i], floty[0][i]);
                hasEnded = false;
            }
            tura++;
        }
        cout << "\n--- KONIEC\n\n";
    }
}
int main()
{
    srand(time(NULL));
    int input[3] = { 1, 1, 0 };
    
    Flota flota1(input[0], input[1], input[2]);
    Flota flota2(input[0], input[1], input[2]);
    walka(&flota1, &flota2);
    return 0;
}

你好, 上述是我的海战模拟器。它对开始时给出的 3 个整数进行操作,每个整数定义参与的相应船舶类别的数量(全部属于母

Flota
类别)。它们之间的差异是统计数据,有些仅出现在后续子类中(如果可能的话,它们会被继承)。 通过
for get()
跳过类演示,生成另一个 Flota 对象,其中包含相同数量的对象。 然后模拟开始。每个对象都与相反数组元素中托管的另一个对象交互。 但是,当至少存在 2 种船舶时,当调用
check victory()
函数且参数是对对象数据的引用时,会发生 SIGSEGV。

这是为什么呢?调用属性本身会导致错误,这是

walka()
函数特有的错误。

请帮忙

编辑:为了复制,在第一个输入中使用,例如,1 1 0,并在下一个输入中输入任何内容(val并不重要)。

编辑2:将用户输入修复为上述组合。

c++ object pointers
3个回答
0
投票

问题在于 for 循环的初始化没有使起始 j 值等于占用槽数 + 1。该片段从 0 开始输入表中的对象,因此输入组合 1 1 0 的结果将导致表具有仅存在 1 个对象(根据上述解释,其中一个将被覆盖)。


0
投票

这里地址消毒剂指向:

/app/example.cpp:163:33: runtime error: member access within null pointer of type 'struct LodzPodwodna'
AddressSanitizer:DEADLYSIGNAL
=================================================================
==1==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000020 (pc 0x000000407190 bp 0x7ffe87ac63e0 sp 0x7ffe87ac6160 T0)
==1==The signal is caused by a READ memory access.
==1==Hint: address points to the zero page.
    #0 0x407190 in walka(Flota*, Flota*) /app/example.cpp:163
    #1 0x408740 in main /app/example.cpp:184
    #2 0x7fe283629d8f  (/lib/x86_64-linux-gnu/libc.so.6+0x29d8f) (BuildId: c289da5071a3399de893d2af81d6a30c62646e1e)
    #3 0x7fe283629e3f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29e3f) (BuildId: c289da5071a3399de893d2af81d6a30c62646e1e)
    #4 0x403394 in _start (/app/output.s+0x403394) (BuildId: 671a5091a9969ac5e5067d77bcefc6c3ca93cbb5)

现在

example.cpp:163
是这一行:

if (checkVictory(floty[0][i]->hp, floty[1][i]->hp, floty[0][i]->nazwa, floty[1][i]->nazwa, floty[0][i]->id, floty[1][i]->id))

根据上面的代码,

floty
的索引范围是正确的:

vector<vector<LodzPodwodna*>> floty(2, vector<LodzPodwodna*>(total));

所以问题一定是指针本身。

这部分代码有可疑之处:

        for (int i = 0; i < 2; i++)
            for (int j = 0; j < args[i]->ships[0]; j++) {
                Okret* okret = args[i]->okrety[j];
                floty[i][j] = new LodzPodwodna(okret->id, okret->nazwa, okret->moc, okret->celnosc, okret->zaloga, okret->celnosc, okret->hp, 1, 130, 1, false);
                floty[i][j]->get();
            }

请注意,索引

j
的范围与
floty
初始化失败的位置不同。在那里,它取决于
total
值,而在有问题的代码中,它取决于
args[i]->ships[0]

注意它们之间的关系:

int total = flota1->ships[0] + flota1->ships[1] + flota1->ships[2];

所以

total
总是比
flota1->ships[0]
flota1->ships[1]
之一更大!

所以你取消引用

nullptr
的底线是因为
total
值与
floty
初始化不一致。


0
投票

使用 valgrind 快速运行告诉我,您在此处的读取无效

if (checkVictory(floty[0][i]->hp, floty[1][i]->hp, floty[0][i]->nazwa, floty[1][i]->nazwa, floty[0][i]->id, floty[1][i]->id))

事实上,你可以验证元素

floty[0][1]
floty[1][1]
nullptr

但是这个错误的根本原因是你正在使用原始指针和手动内存管理

© www.soinside.com 2019 - 2024. All rights reserved.