Как читать из файла в динамически размещенный связанный список указателей на структуру? - PullRequest
0 голосов
/ 09 мая 2018

Итак, я некоторое время работал над этой программой и дошел до того, что она прекрасно компилируется, но когда я запускаю ее, я получаю Сегментацию. Я отследил ошибку через gdb до приведенной ниже функции, но не могу на всю жизнь понять, в чем проблема, и я недостаточно разбираюсь в gdb, чтобы определить какие-либо подробности о происхождении ошибки. Как в вопросе говорится, что я пытаюсь прочитать файл из динамически размещенного связанного списка, где каждый узел списка имеет указатель на структуру с именем Element. Затем мне нужно преобразовать этот связанный список в массив указателей для структуры Element. Я надеюсь, что свежая пара глаз увидит какую-то ошибку, которую я допустил из-за того, что был слеп.

ОБНОВЛЕНИЕ: Добавлены остальные исходные файлы. Добавлен gdb фрагмент трассировки.

Element.h

#ifndef ELEMENT_H
#define ELEMENT_H
#include <string>
using std::string;

struct Element
{
    string name;
    int atomNum;
    double mass;
    string abbr; // abbreviation
};

Element **get_table(string file, int& size);

#endif /* ELEMENT_H */

get_table.cpp

#include "Element.h"
#include <iostream>
#include <fstream>
#include <string>

using namespace std;

struct Node
{
    Element *elem;
    Node *next;
};

Element **get_table(string filename, int &size)
{
    ifstream infile;
    string nextline;
    size = 0;
    infile.open(filename.c_str());
    Node *head = NULL;

    if(infile)
    {
        do{
            getline(infile, nextline, ' ');
            Node *node = new Node;
            node->elem = new Element;
            node->elem->atomNum = stoi(nextline.c_str());
            getline(infile, nextline, ' ');
            node->elem->abbr = nextline;
            getline(infile, nextline, ' ');
            node->elem->mass = stod(nextline.c_str());
            getline(infile, nextline);
            node->elem->name = nextline;
            node->next = head;
            head = node;
            size++;
        }while(infile);
    }
    else
    {
        infile.close();
        return NULL;
    }

    //convert linked list into array of pointers
    Element **table = new Element*[size];
    int i = 0;
    Node *curr = head;
    while(curr->next != NULL)
    {
        table[i] = curr->elem;
        curr = curr->next;
        i++;
    }

    //deallocate linked list
    while(head != NULL)
    {
        Node* temp = head->next;
        delete head;
        head = temp;
    }

    infile.close();
    return table;
}

periodic_table.cpp

#include <cstdlib>
#include <iostream>
#include <iomanip>
#include "Element.h"

using namespace std;

int compare_by_name(const void* e1, const void* e2) 
{
    struct Element *left = *(Element**) e1;
    struct Element *right = *(Element**) e2;

    if (left->name < right->name)
        return -1;
    else if (left->name > right->name)
        return 1;
    else
        return 0;
}

int printTable(Element** tab, const int size) 
{
    cout << "Name" << setw(23) << "Abr" << setw(4) << "Ano" << setw(7) << "Mass" << endl;
    cout << endl << "----------------------- --- --- ------" << endl;
    cout << fixed;
    for (int i = 0; i < size; i++) {
        cout << setw(23) << left << tab[i]->name;
        cout << setw(3) << left << tab[i]->abbr;
        cout << setprecision(0) << setw(4) << right << tab[i]->atomNum;
        cout << setprecision(2) << setw(7) << right << tab[i]->mass << endl;
    }
    return 0;
}

double getAvgMass(Element** table, const int size) 
{
    double sum = 0.0;
    if (size == 0)
        return sum;
    else
        for (int i = 0; i < size; i++) {
            sum += table[i]->mass;
        }
    return sum / size;
}

/*
 * 
 */
int main() 
{
    string filename = "/user/tvnguyen7/data/periodictable.dat";
    string tempfile = "periodictable.dat";
    filename = tempfile;
    int size = 0;
    Element **table = get_table(filename, size);

    if (table != NULL) //sort table by element name and print in alphabetical order
    {
        qsort(table, size, sizeof (Element*), compare_by_name);
        printTable(table, size);
        //calculate and display average mass of elements in table
        cout << setw(23) << left << "The average mass =" << fixed << setprecision(2) << setw(14) << right << getAvgMass(table, size) << endl;
    }
    delete [] table;
    table = nullptr;

    return 0;
}

ИСПРАВЛЕНО: GDB Backtrace

    (gdb) run
Starting program: /user/jscanning/proj2.out
terminate called after throwing an instance of 'std::invalid_argument'
  what():  stoi

Program received signal SIGABRT, Aborted.
0x00007ffff712ff65 in raise () from /lib64/libc.so.6
(gdb) bt
#0  0x00007ffff712ff65 in raise () from /lib64/libc.so.6
#1  0x00007ffff7131c8a in abort () from /lib64/libc.so.6
#2  0x00007ffff7a93ecd in __gnu_cxx::__verbose_terminate_handler() ()
   from /usr/lib/gcc/x86_64-pc-linux-gnu/5.4.0/libstdc++.so.6
#3  0x00007ffff7a91d36 in ?? ()
   from /usr/lib/gcc/x86_64-pc-linux-gnu/5.4.0/libstdc++.so.6
#4  0x00007ffff7a91d81 in std::terminate() ()
   from /usr/lib/gcc/x86_64-pc-linux-gnu/5.4.0/libstdc++.so.6
#5  0x00007ffff7a91f98 in __cxa_throw ()
   from /usr/lib/gcc/x86_64-pc-linux-gnu/5.4.0/libstdc++.so.6
#6  0x00007ffff7abb2cf in std::__throw_invalid_argument(char const*) ()
   from /usr/lib/gcc/x86_64-pc-linux-gnu/5.4.0/libstdc++.so.6
#7  0x000000000040232f in __gnu_cxx::__stoa<long, int, char, int> (
    __convf=0x401490 <strtol@plt>, __name=0x402573 "stoi", __str=0x7fffffffdd50 "\n", __idx=0x0)
    at /usr/lib/gcc/x86_64-pc-linux-gnu/5.4.0/include/g++-v5/ext/string_conversions.h:65
#8  0x0000000000402258 in std::__cxx11::stoi (__str="\n", __idx=0x0, __base=10)
    at /usr/lib/gcc/x86_64-pc-linux-gnu/5.4.0/include/g++-v5/bits/basic_string.h:5260
#9  0x0000000000401fa2 in get_table (filename="/user/tvnguyen7/data/periodictable.dat",
    size=@0x7fffffffdfa4: 118) at get_table.cpp:54
#10 0x0000000000401ab0 in main () at periodic_table.cpp:78
(gdb)

periodictable.dat

1 H 1.00794 Hydrogen
2 He 4.002602 Helium
3 Li 6.941 Lithium
4 Be 9.012182 Beryllium
5 B 10.811 Boron
6 C 12.0107 Carbon
7 N 14.0067 Nitrogen
8 O 15.9994 Oxygen
9 F 18.9984032 Fluorine
10 Ne 20.1797 Neon
11 Na 22.98976928 Sodium
12 Mg 24.305 Magnesium
13 Al 26.9815386 Aluminum
14 Si 28.0855 Silicon
15 P 30.973762 Phosphorus
16 S 32.065 Sulfur
17 Cl 35.453 Chlorine
18 Ar 39.948 Argon
19 K 39.0983 Potassium
20 Ca 40.078 Calcium
21 Sc 44.955912 Scandium
22 Ti 47.867 Titanium
23 V 50.9415 Vanadium
24 Cr 51.9961 Chromium
25 Mn 54.938045 Manganese
26 Fe 55.845 Iron
27 Co 58.933195 Cobalt
28 Ni 58.6934 Nickel
29 Cu 63.546 Copper
30 Zn 65.38 Zinc
31 Ga 69.723 Gallium
32 Ge 72.64 Germanium
33 As 74.9216 Arsenic
34 Se 78.96 Selenium
35 Br 79.904 Bromine
36 Kr 83.798 Krypton
37 Rb 85.4678 Rubidium
38 Sr 87.62 Strontium
39 Y 88.90585 Yttrium
40 Zr 91.224 Zirconium
41 Nb 92.90638 Niobium
42 Mo 95.96 Molybdenum
43 Tc 98 Technetium
44 Ru 101.07 Ruthenium
45 Rh 102.9055 Rhodium
46 Pd 106.42 Palladium
47 Ag 107.8682 Silver
48 Cd 112.411 Cadmium
49 In 114.818 Indium
50 Sn 118.71 Tin
51 Sb 121.76 Antimony
52 Te 127.6 Tellurium
53 I 126.90447 Iodine
54 Xe 131.293 Xenon
55 Cs 132.9054519 Cesium
56 Ba 137.327 Barium
57 La 138.90547 Lanthanum
58 Ce 140.116 Cerium
59 Pr 140.90765 Praseodymium
60 Nd 144.242 Neodymium
61 Pm 145 Promethium
62 Sm 150.36 Samarium
63 Eu 151.964 Europium
64 Gd 157.25 Gadolinium
65 Tb 158.92535 Terbium
66 Dy 162.5 Dysprosium
67 Ho 164.93032 Holmium
68 Er 167.259 Erbium
69 Tm 168.93421 Thulium
70 Yb 173.054 Ytterbium
71 Lu 174.9668 Lutetium
72 Hf 178.49 Hafnium
73 Ta 180.94788 Tantalum
74 W 183.84 Tungsten
75 Re 186.207 Rhenium
76 Os 190.23 Osmium
77 Ir 192.217 Iridium
78 Pt 195.084 Platinum
79 Au 196.966569 Gold
80 Hg 200.59 Mercury
81 Tl 204.3833 Thallium
82 Pb 207.2 Lead
83 Bi 208.9804 Bismuth
84 Po 209 Polonium
85 At 210 Astatine
86 Rn 222 Radon
87 Fr 223 Francium
88 Ra 226 Radium
89 Ac 227 Actinium
90 Th 232.03806 Thorium
91 Pa 231.03588 Protactinium
92 U 238.02891 Uranium
93 Np 237 Neptunium
94 Pu 244 Plutonium
95 Am 243 Americium
96 Cm 247 Curium
97 Bk 247 Berkelium
98 Cf 251 Californium
99 Es 252 Einsteinium
100 Fm 257 Fermium
101 Md 258 Mendelevium
102 No 259 Nobelium
103 Lr 262 Lawrencium
104 Rf 267 Rutherfordium
105 Db 268 Dubnium
106 Sg 271 Seaborgium
107 Bh 272 Bohrium
108 Hs 270 Hassium
109 Mt 276 Meitnerium
110 Ds 281 Darmstadtium
111 Rg 280 Roentgenium
112 Cn 285 Copernicium
113 Uut 284 Ununtrium
114 Uuq 289 Ununquadium
115 Uup 288 Ununpentium
116 Uuh 293 Ununhexium
117 Uus 294 Ununseptium
118 Uuo 294 Ununoctium

ОБНОВЛЕНИЕ: Когда я компилирую с -g и -o, я получаю это:

/tmp/cccoH75A.o: In function `main':
/user/jscanning/periodic_table.cpp:81: undefined reference to `get_table(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, int&)'
collect2: error: ld returned 1 exit status

Что это значит?

1 Ответ

0 голосов
/ 09 мая 2018

Хорошая новость в том, что вы были очень, очень близки. Плохая новость - это «действительно очень близкая» часть - требуется всего одна тонкая ошибка, чтобы торпедировать ваш код.

Сначала общее обсуждение. Хотя в использовании указатель на указатель на элемент нет ничего плохого, вы упускаете преимущества использования C ++ и автоматической обработки памяти, предлагаемой <vector>. Тем не менее, это хорошо знать, как справиться с обоими.

Ваша ошибка:

while(curr->next != NULL)

Здесь, при поддержке вашего связного списка для назначения указателей на table, вы не можете выполнить итерацию и включить водород, остановив один элемент до Helium. Это оставляет ваш последний указатель как неинициализированный (неопределенный) указатель . Когда вы передаете элементы в qsort, вы SegFault. Чтобы обеспечить правильное включение H, вам необходимо:

while(curr != NULL)

(curr содержит действительные данные до NULL, а не curr->next = NULL)

Кроме того, как уже говорилось, ваше чтение не может быть выполнено из-за того, что вы не проверяете каждый вызов getline. Лучшим подходом было бы прочитать каждую строку из файла в nextline, а затем использовать stringstream, чтобы разделить значения во временные переменные перед выделением каждого node. Там вы проверяете свои чтения и проверяете ваш анализ до выделения памяти . Вы можете сделать что-то вроде:

Element **get_table (string filename, int& size)
{
    ifstream infile;
    string nextline;
    size = 0;
    infile.open(filename.c_str());
    Node *head = NULL;

    if (!infile.is_open()) {
        cerr << "file open failed '" << filename << "'\n";
        return NULL;
    }

    while (getline (infile, nextline)) {
        stringstream s (nextline);
        string name, abbr;
        int atomNum;
        double mass;

        if ((s >> atomNum >> abbr >> mass >> name)) {
            Node *node = new Node;
            node->elem = new Element;
            node->elem->atomNum = atomNum;
            node->elem->abbr = abbr;
            node->elem->mass = mass;
            node->elem->name = name;
            node->next = head;
            head = node;
            size++;
        }
        else {
            /* handle error or warn on failed parse as desired */
        }
    }

Далее просто примечание о значениях жесткого кодирования - не надо. Если вам нужны константы (включая имена файлов), то #define они находятся вверху вашего исходного файла, так что есть одно удобное место для изменения значений при изменении ваших потребностей, например,

periodic_table.cpp

#include "Element.h"

#define FNAME "dat/periodictable.dat"
...
int main(void)  {

    int size = 0;
    Element **table = get_table (FNAME, size);

В целом, ваш код предоставляет хорошо отформатированную периодическую таблицу, например,

Пример использования / Вывод

$ ./bin/periodic_table
Name                    Abr Ano   Mass

----------------------- --- --- ------
Actinium               Ac   89 227.00
Aluminum               Al   13  26.98
Americium              Am   95 243.00
Antimony               Sb   51 121.76
Argon                  Ar   18  39.95
Arsenic                As   33  74.92
Astatine               At   85 210.00
...
Ununtrium              Uut 113 284.00
Uranium                U    92 238.03
Vanadium               V    23  50.94
Xenon                  Xe   54 131.29
Ytterbium              Yb   70 173.05
Yttrium                Y    39  88.91
Zinc                   Zn   30  65.38
Zirconium              Zr   40  91.22
The average mass =             146.44

(вы можете настроить ширину выходного поля (setw(24)), чтобы привести в порядок расстояние между столбцами под заголовком)

Посмотрите вещи и дайте мне знать, если у вас есть вопросы.


.. а если вы хотите узнать секретную, мощную технику отладки, которая раскрыла ошибку?

    while (curr->next != NULL)
    {
        cerr << "assigning table[" << i << "]: " << curr->elem->abbr << '\n';
        table[i] = curr->elem;
        curr = curr->next;
        i++;
    }

Я просто вывел на экран аббревиатуры, присвоенные table, и остановился на:

assigning table[116]: He
Segmentation fault (core dumped)

Код, используемый в приведенном выше примере

Element.h

Не определять using xxx в заголовках. Это будет включать в себя все пространство имен в каждом файле, включающем заголовок.

#ifndef ELEMENT_H
#define ELEMENT_H
#include <string>

struct Element
{
    std::string name;
    int atomNum;
    double mass;
    std::string abbr; // abbreviation
};

Element **get_table (std::string file, int& size);

#endif /* ELEMENT_H */

get_table.cpp

#include "Element.h"
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <vector>

using namespace std;

struct Node
{
    Element *elem;
    Node *next;
};

Element **get_table (string filename, int& size)
{
    ifstream infile;
    string nextline;
    size = 0;
    infile.open(filename.c_str());
    Node *head = NULL;

    if (!infile.is_open()) {
        cerr << "file open failed '" << filename << "'\n";
        return NULL;
    }

    while (getline (infile, nextline)) {
        stringstream s (nextline);
        string name, abbr;
        int atomNum;
        double mass;

        if ((s >> atomNum >> abbr >> mass >> name)) {
            Node *node = new Node;
            node->elem = new Element;
            node->elem->atomNum = atomNum;
            node->elem->abbr = abbr;
            node->elem->mass = mass;
            node->elem->name = name;
            node->next = head;
            head = node;
            size++;
        }
    }

    //convert linked list into array of pointers
    Element **table = new Element*[size];
    int i = 0;
    Node *curr = head;
    while (curr != NULL)    /* error here - curr, not curr->next */
    {
        // cerr << "table[" << i << "]: " << curr->elem->abbr << '\n';
        table[i] = curr->elem;
        curr = curr->next;
        i++;
    }

    //deallocate linked list
    while(head != NULL)
    {
        Node* temp = head->next;
        delete head;
        head = temp;
    }

    infile.close();
    return table;
}

periodic_table.cpp

#include <cstdlib>
#include <iostream>
#include <iomanip>
#include "Element.h"

#define FNAME "dat/periodictable.dat"

using namespace std;

int compare_by_name(const void* e1, const void* e2) 
{
    struct Element *left = *(Element**) e1;
    struct Element *right = *(Element**) e2;

    if (left->name < right->name)
        return -1;
    else if (left->name > right->name)
        return 1;
    else
        return 0;
}

int printTable(Element** tab, const int size) 
{
    cout << "Name" << setw(23) << "Abr" << setw(4) << "Ano" << 
            setw(7) << "Mass" << endl;
    cout << endl << "----------------------- --- --- ------" << endl;
    cout << fixed;
    for (int i = 0; i < size; i++) {
        cout << setw(24) << left << tab[i]->name;
        cout << setw(3) << left << tab[i]->abbr;
        cout << setprecision(0) << setw(4) << right << tab[i]->atomNum;
        cout << setprecision(2) << setw(7) << right << tab[i]->mass << endl;
    }
    return 0;
}

double getAvgMass(Element** table, const int size) 
{
    double sum = 0.0;
    if (size == 0)
        return sum;
    else
        for (int i = 0; i < size; i++) {
            sum += table[i]->mass;
        }
    return sum / size;
}

/*
 * 
 */
int main()  {

    int size = 0;
    Element **table = get_table (FNAME, size);

    if (table != NULL) //sort table alphabetically by element name and print
    {
        qsort (table, size, sizeof (Element*), compare_by_name);
        printTable(table, size);
        //calculate and display average mass of elements in table
        cout << setw(24) << left << "The average mass =" << 
                fixed << setprecision(2) << setw(14) << right << 
                getAvgMass(table, size) << endl;
    }
    delete [] table;
    table = nullptr;

    return 0;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...