如何从文件中读入动态分配的结构指针链表?
How to read from a file into a dynamically allocated linked list of pointers to struct?
所以我已经在这个程序上工作了一段时间,我已经把它编译得很好,但是当我 运行 它时,我得到了一个分段错误。
我已经通过 gdb 将故障回溯到下面的函数,但我终究无法看到问题出在哪里,而且我对 gdb 的了解还不足以确定有关故障起源的更多详细信息。就像问题说的那样,我正在尝试从一个文件中读取一个动态分配的链表,其中链表的每个节点都有一个指向名为 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 回溯
(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
这是什么意思?
好消息是你们真的非常亲密。坏消息是 "really really close" 部分——它只需要一个细微的错误就可以破坏您的代码。
首先是一般性讨论。虽然使用 pointer-to-pointer-to-Element 没有任何问题,但您却失去了使用 C++ 和 <vector>
提供的自动内存处理的好处。也就是说,知道如何处理这两者是很好的。
你的错误是:
while(curr->next != NULL)
在这里,虽然你的 linked-list 支持将指针分配给 table
,但你未能迭代并包含 Hydrogen,在 Helium
之前停止了一个元素。这使您的最后一个指针成为 未初始化(不确定)指针 。当您将元素传递给 qsort
时,您会发生 SegFault。为确保正确包含 H
,您需要:
while(curr != NULL)
(curr
包含有效数据直到 NULL
,而不是 curr->next = NULL
)
此外,正如所讨论的那样,由于未检查对 getline
的每次调用,您的读取可能会失败。更好的方法是将文件中的每一行读入 nextline
,然后在分配每个 node
之前使用 stringstream
将值分隔为临时变量。在分配内存之前,您验证您的读取并验证您的解析。你可以这样做:
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);
总而言之,您的代码提供了格式良好的周期性 table,例如
例子Use/Output
$ ./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)
) 以整理标题下的列间距)
检查一下,如果您有任何问题,请告诉我。
..如果您想知道秘密,high-powered 揭示错误的调试技术?
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)
上面例子中的代码As-Used
Element.h
不要在 header 中定义 using xxx
。这将在每个包含 header.
的文件中包含整个命名空间
#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;
}
所以我已经在这个程序上工作了一段时间,我已经把它编译得很好,但是当我 运行 它时,我得到了一个分段错误。 我已经通过 gdb 将故障回溯到下面的函数,但我终究无法看到问题出在哪里,而且我对 gdb 的了解还不足以确定有关故障起源的更多详细信息。就像问题说的那样,我正在尝试从一个文件中读取一个动态分配的链表,其中链表的每个节点都有一个指向名为 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 回溯
(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
这是什么意思?
好消息是你们真的非常亲密。坏消息是 "really really close" 部分——它只需要一个细微的错误就可以破坏您的代码。
首先是一般性讨论。虽然使用 pointer-to-pointer-to-Element 没有任何问题,但您却失去了使用 C++ 和 <vector>
提供的自动内存处理的好处。也就是说,知道如何处理这两者是很好的。
你的错误是:
while(curr->next != NULL)
在这里,虽然你的 linked-list 支持将指针分配给 table
,但你未能迭代并包含 Hydrogen,在 Helium
之前停止了一个元素。这使您的最后一个指针成为 未初始化(不确定)指针 。当您将元素传递给 qsort
时,您会发生 SegFault。为确保正确包含 H
,您需要:
while(curr != NULL)
(curr
包含有效数据直到 NULL
,而不是 curr->next = NULL
)
此外,正如所讨论的那样,由于未检查对 getline
的每次调用,您的读取可能会失败。更好的方法是将文件中的每一行读入 nextline
,然后在分配每个 node
之前使用 stringstream
将值分隔为临时变量。在分配内存之前,您验证您的读取并验证您的解析。你可以这样做:
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);
总而言之,您的代码提供了格式良好的周期性 table,例如
例子Use/Output
$ ./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)
) 以整理标题下的列间距)
检查一下,如果您有任何问题,请告诉我。
..如果您想知道秘密,high-powered 揭示错误的调试技术?
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)
上面例子中的代码As-Used
Element.h
不要在 header 中定义 using xxx
。这将在每个包含 header.
#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;
}