如何从文件读取到动态分配的指向结构的指针链表中?
How to read from a file into a dynamically allocated linked list of pointers to struct?
所以我已经在这个程序上工作了一段时间,我已经把它弄到了一个编译得很好的地步,但是当我运行它时,我得到了一个分段错误。 我已经通过 gdb 将故障回溯到下面的函数,但终生都看不到问题所在,而且我对 gdb 不够精通,无法确定有关故障起源的更多详细信息。就像问题所说的那样,我正在尝试从文件读取到动态分配的链表中,其中列表的每个节点都有一个指向名为 Element 的结构的指针。然后我需要将该链表转换为指向结构 Element 的指针数组。我希望一双新鲜的眼睛能看到我所犯的一些错误,而这些错误是我视而不见的。
更新:添加了其余的源文件。添加了gdb
回溯代码段。
元素.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)
定期.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
这是什么意思?
好消息是你真的非常非常接近。坏消息是"非常接近"的部分 - 只需要一个微妙的错误就可以破坏你的代码。
首先是一般性讨论。虽然使用指针到指针到元素没有错,但您错过了使用 C++ 的好处和<vector>
提供的自动内存处理。也就是说,知道如何处理两者是件好事。
您的错误是:
while(curr->next != NULL)
在这里,通过您的链接列表将指针分配给table
,您无法迭代并包含 Hydrogen,在Helium
之前停止一个元素。这会将最后一个指针保留为未初始化(不确定(指针。当你把元素传递给qsort
时,你隔离了错误。要确保正确包含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);
总而言之,您的代码提供了一个格式良好的元素周期表,例如
示例使用/输出
$ ./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)
上面示例中使用的代码
元素.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;
}
- 如何在 C# 中映射双 C 结构指针?
- C++:映射结构中的结构指针
- C++:添加新结构时,结构指针向量中的所有元素都会更新
- 类的静态结构指针声明在C++
- 在两个.cpp文件之间定义全局类/结构指针
- 返回对常量结构(指针类型)成员的引用:明显的左值到右值转换
- 是否可以将无符号字符数组reinterpret_cast到仅包含C++中无符号字符成员的结构指针
- 如何在地图中使用结构指针
- 结构指针在"新"调用上给出已分配的地址?
- 铸造结构指针指向C 的工会指针
- c++ 结构指针在初始化为 NULL 时无法读取内存
- 函数中的结构指针的地址为0x1
- C 用结构指针初始化结构
- C 结构指针铸成数组Interop C#
- 方法中不允许访问结构指针
- 如何打印出结构指针
- MPI_Op_create:候选功能不可行.自定义结构指针不能解释为空指针
- 尝试将结构指针传递给类时出错
- 初始化结构指针的值
- 使用 pybind11 将自定义结构(指针向量)从 C++ 传递到 python 的方法是什么?