getting a segmentation fault

The code compiles properly though when I run it it causes a segmentation fault.
I don't know what I did wrong, can somebody please explain what I did wrong?

the split function just splits the textfile into seperate strings (between whitespaces) and puts them in a vector. And the student_info is a struct.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include <iostream>
#include <algorithm>
#include <iomanip>
#include <ios>
#include <stdexcept>
#include <string>
#include <vector>
#include <fstream>
#include <list>
#include <cctype>
#include <cstdlib>
#include "grade.h"
#include "Student_info.h"
#include "split.h"

using std::cin;
using std::cout;
using std::domain_error;
using std::endl;
using std::max;

using std::setprecision;
using std::sort;
using std::streamsize;
using std::string;
using std::vector;
using std::list;
using std::ifstream;
using std::isalpha;
using std::stoi;

int main()
{

	list<Student_info> students;
	Student_info record;
	string::size_type maxlen = 0;

	ifstream inFile;
	inFile.open("D:\\projects\\grades\\text.txt");
	if(!inFile)
		cout << "Unable to open file text.txt";
	
	double x;
	string line;
	while(getline(inFile, line)){
		vector<string> v(split(line));	
		for(vector<string>::size_type i = 0; i !=v.size(); ++ i){
			v[i] = record.name;
			v[i + 1] = record.midterm;
			v[i + 2] = record.final;
                        //this loop will fill up the homework vector with numbers until it gets to a alphabetic character
			for(vector<string>::size_type j = i+3; !isalpha(v[j][0]); ++j){
				record.homework.push_back(stoi(v[j]));		
			}
		students.push_back(record);
		}	
		inFile.close();
	}

	for(list<Student_info>::size_type i = 0; i != students.size(); ++i){
		list<Student_info>::iterator iter = students.begin();
		cout << iter->name << iter->midterm << iter->final << endl;
		for(vector<Student_info>::size_type j = 0; j != iter->homework.size();
						++j)
			cout << iter->homework[j] << endl;
		iter++;
	}
	return 0;

}
Last edited on
Looks like you are accessing vector elements out of bounds on lines 51-54.
Hello alfie nsugh,

With out the files; "grade.h", "Student_info.h", "split.h" and "text.txt" there is no way to test the program.

Lin 48 in the for loop "vector<string>::size_type" can be shortened to size_t" which is what is returned by "v.size()" . And inside the for loop did you mean "record.name = v[i]"?

The for loop at line 53 I would not even begin to guess about it without knowing more.

As I said there is no way to test the program.

Hope that helps for now,

Andy
I tried changing i !=v.size() to i+3 != v.size(), but that didn't do the job. How should I resolve this issue?
Got it to work partially, I changed the outer for as described above, and I added || j != v.size() to the inner for loop. Thanks.

Now I got another compilation error though: terminate called after throwing an instance of 'std::invalid_argument'
what(): stoi

1
2
3
4
5
6
7
8
9
10
11
#ifndef GUARD_split_h
#define GUARD_split_h

//split.h
#include <iostream>
#include <vector>
#include <cctype>

std::vector<std::string> split(const std::string&);

#endif 

split.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>
#include <vector>
#include <cctype>

using std::string;
using std::vector;
using std::cin;
using std::cout;
using std::endl;

vector<string> split(const string& s)
{
	vector<string> ret;
	typedef string::size_type string_size;
	string_size i = 0;

	//invariant: we have processed characters [original value of i, i)
	while (i != s.size()){
		// ignore leading blanks
		// invariant: characters in range [original i, current i)
	while (i != s.size() && isspace(s[i]))
		++i;

	// find end of next word
	string_size j = i;
	// invariant: none of the characters in range [original j, current j)
	while (j != s.size() && !isspace(s[j]))
		j++;
		// if we found some nonwhitespace characters
		if(i != j){
			//copy from s starting at i and taking j-i chars
			ret.push_back(s.substr(i, j - i));
			i = j;
		}
	}
	return ret;
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21


#ifndef GUARD_Student_info
#define GUARD_Student_info

//Student_info.h header file

#include<iostream>
#include<string>
#include<vector>

struct Student_info {
	std::string name;
	double midterm, final;
	std::vector<double> homework;
};

bool compare(const Student_info&, const Student_info&);
std::istream& read(std::istream&, Student_info&);
std::istream& read_hw(std::istream&, std::vector<double>&);
#endif 


student info cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//source file for Student_info-related functions
#include "Student_info.h"

using std::istream; using std::vector;

bool compare(const Student_info& x, const Student_info& y)
{
	return x.name < y.name;
}

istream& read(istream& is, Student_info& s)
{
	// read and store the students name and midterm and final exam grades
	is >> s.name >> s.midterm >> s.final;
	
	read_hw(is, s.homework);
	return is;
}

//read homework grades from an input stream into a vector
istream& read_hw(istream& in, vector<double>& hw)
{
	if(in){
		//get rid of previous contents
		hw.clear();
		
		//read homework grades
		double x;
		while(in >> x)
			hw.push_back(x);
		
		//clear the stream so that input will work for the next students
		in.clear();
	}
	return in;
}


I am not including the code of grade.h and grade.cpp because that is not used in by this program yet. Thanks in advance
Last edited on

echo > grade.h
g++.exe grade.cpp split.cpp student_info.cpp -o grade
 ./grade.exe
Unable to open file text.txt


It runs when I test it :D


echo > text.txt
 ./grade.exe
Unable to open file text.txt


Uh oh. Oh, here's the problem:
 
inFile.open("D:\\projects\\grades\\text.txt");

1. That's not how I access drive D when in cygwin.
2. Most windows users don't have a drive D.
3. Most windows users won't be saving their projects there either.

Thanks for the reminder not to copy and paste code then run it without at least looking at it though. I need to be more careful about that.

OK with the path removed:
inFile.open("text.txt");

I get no output at all, probably because the text file is empty. What should we fill it with to test it? Also can you post your updated grade.cpp so we will be testing the same thing?
text.txt
 
firstStudent 7 9 5 4 8 5 9 7 8 6 5 4 secondStudent 7 8 5 7 8 6 77 89 6 78 68 77 thirdStudent 8 9 8 8 7 8 89 7 7 7 7

This is the textfile.
Thanks! Now I'm getting the segfault and can start tracking down why.
1
2
3
4
5
6
7
8
9
10
11
12
..
        while(getline(inFile, line)){
                cout << line << endl; //DEBUG
                vector<string> v(split(line));
                for(vector<string>::size_type i = 0; i !=v.size(); ++i){
                        v[i] = record.name;
                        v[i + 1] = record.midterm;
                        v[i + 2] = record.final;
                        cout << "v:" << v[i] << endl; //DEBUG
                        cout << "v+1:" << v[i+1] << endl; //DEBUG
                        cout << "v+2:" << v[i+2] << endl; //DEBUG
..

result (as seen piped into less):

firstStudent 7 9 5 4 8 5 9 7 8 6 5 4 secondStudent 7 8 5 7 8 6 77 89 6 78 68 77 thirdStudent 8 9 8 8 7 8 89 7 7 7 7
v:
v+1:^@
v+2:^@
v:
v+1:^@
v+2:^@
v:
v+1:^@
v+2:^@
v:
v+1:^@
v+2:^@

...this over and over and over until segfault

What the debug statements I added tell me:
Whatever it was trying to assign there, it never knew in the first place.
It's going on and on and on, and judging by the text file it ought to stop after only a few passes thru the loop.

It reads the entire line all at once into "line", but doesn't ever assign anything to the values in the record class using that read line, prior to trying to use the values to assign data into the array.

Hello alfie nsugh,

Just started working on the program and ran into some questions.

Line 49, 50 and 51 in main. At this point "record" is empty" and my question is why set "v[i]" equal to nothing? I believe you meant record.name = v[i] and the same for the next two lines.

In line 53 when did "v" become a 2D vector? this is where I received the "segmentation fault" error trying to access a 2D vector that does not exist.

That is as far as I managed to get for now.

One last thought I had in regards to the input file. I realize that the program is designed to work with a single line, but it would make things easier if each name and numbers was on one line. If you are stuck with what you were given that is fine.

Hope that helps,

Andy
Hello alfie nsugh,

I did get the program to work. Instead of trying to explain everything I let the code and comments do the job.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
	while (getline(inFile, line))
	{
		vector<string> v(split(line));

		for (vector<string>::size_type i = 0; i < v.size(); i += 13)  // <--- Changed the 3rd part to advance to the next name.
		{
			record.name = v[i];
			record.midterm = stod(v[i + 1]);
			record.final = stod(v[i + 2]);

			//v[i] = record.name;
			//v[i + 1] = record.midterm;
			//v[i + 2] = record.final;

			//this loop will fill up the homework vector with numbers until it gets to a alphabetic character
			//for (vector<string>::size_type j = stoi(v[i + 3]); !isalpha((v[j][0])); ++j)

			for (vector<string>::size_type j = start; !isalpha((v[j][0])); ++j)  // <--- Needs to start at 3 then increase to the same point of the next name.
			{
				// 5 4 8 5 9 7 8 6 5 4
				record.homework.push_back(stod(v[j]));
			}

			students.push_back(record);
			record.homework.clear();
			start += 13;
		}
		inFile.close();
	}

	list<Student_info>::iterator iter = students.begin();  // Needs done outside the for loop. Otherwise where it was it would always start at the beginning never changing.

	for (list<Student_info>::size_type i = 0; i < students.size(); ++i)
	{
		cout << iter->name << ", " << iter->midterm << ", " << iter->final << endl;

		for (vector<Student_info>::size_type j = 0; j != iter->homework.size(); ++j)
			cout << iter->homework[j] << ' ';

		std::cout << '\n' << std::endl;

		iter++;
	}

	// This is used to keep the console window open before return ends the program.
	// The next line may not be needed. If you have to press "enter" to see the prompt it is not needed.
	//std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');  // <--- Requires header file <limits>.
	std::cout << "\n\n Press Enter to continue";
	std::cin.get();

Line 18 still needs to be fixed or you will have the same numbers for each student.

Hope that helps,

Andy
I still get: terminate called after throwing an instance of 'std::invalid_argument'
what(): stod

The distance between names isn't the same I tried to check wether the first part of the string that is part of the vector is a alphabetic character with the method as described here:

https://stackoverflow.com/questions/29129165/get-first-character-of-a-string-from-a-string-vector
An std::invalid_argument exception is thrown when the string that was passed to std::stod is not formatted correctly.

1
2
3
4
std::stod("1.2") // OK, returns 1.2.
std::stod("     5abc") // OK, returns 5.0.
std::stod("abc123") // Error, throws std::invalid_argument exception.
std::stod("") // Error, throws std::invalid_argument exception. 


I'm not sure how your code looks like at the moment, but just to rule out that you are not still accessing vector elements out of bounds you can use the following compiler flag: -D_GLIBCXX_DEBUG

This will stop your program as soon you access a vector element out of bounds with a very informative error message.
Hello alfie nsugh,

The second for loop inside the while loop: for (vector<string>::size_type j = start; !isalpha((v[j][0])); ++j). I did figure out what (v[j][0] is doing which I figured out what it was when I started working with the program. Thank you for the link I still have to finish reading it.

What happens her is the for loop ends when it finds a letter at the beginning of a vector element. This works good for the first and second name, but not for the third name. It does extract all the necessary information for the third name and all the numbers, but has no idea when to end because it never finds a letter to stop on.

When I added the word "end" to the end of the input file it worked fine. And everything else worked the way it should.

I changed the output a bit and this is what I got:
firstStudent, 7, 9
5 4 8 5 9 7 8 6 5 4

secondStudent, 7, 8
5 7 8 6 77 89 6 78 68 77

thirdStudent, 8, 9
8 8 7 8 89 7 7 7 7



 Press Enter to continue

The two numbers next to the name are "midterm" and "final". You can adjust the output to describe this better.

Hope that helps,

Andy
Sorry for taking so long to react, thank you for helping out. Which value did you give to start? And why is i += 13? not all names are 13 words away
Last edited on
Hello alfie nsugh,

My bad, I was thinking to far ahead of where I was working again. "start" was defined and initialized to a value of 3. Because in the vector the name is at element zero, midterm at element 1 and final at element 2.

May be by accident name 1, name 2 and name 3 just happened to be spaced 13 apart which happens to work fine for what you have right now. Should this spacing ever change your code would not likely work.

While looking in the "Student_info.cpp" file I noticed two functions that are never used, so I used them. I did have to change the functions around, but kept the concept.

To start with I changed the input file to:
firstStudent 7 9 5 4 8 5 9 7 8 6 5 4
secondStudent 7 8 5 7 8 6 77 89 6 78 68 77
John_Doe 8 9 50 77 66 85 80 75 91
thirdStudent 8 9 8 8 7 8 89 7 7 7 7
Jane_Doe 100 100 100 100 100 100 100 100 100 100 100 100


And yes that is a blank line at the end of the file.

In main all I had to do is:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
int main()
{
        // <--- Removed unused variables.
	list<Student_info> students;
	Student_info record;
	string line;

	ifstream inFile;
	//inFile.open("D:\\projects\\grades\\text.txt");
	//inFile.open("text.txt");
	inFile.open("text 2.txt");  // <--- One line per name.

        // <--- This should work better for you.
	if (!inFile)
	{
		cout << "Unable to open file text.txt";
		std::this_thread::sleep_for(std::chrono::seconds(5));  // Requires header files "chrono" and "thread"
		exit(1);  // <--- no need to continue.
	}

	while (getline(inFile, line))
	{
		vector<string> v(split(line));

		read(record, v);

		students.push_back(record);
		record.homework.clear();

		//std::cout << std::endl;  // <--- Used for debugging as a break point.
	}
	
	inFile.close();

        // <--- Rest of code for printing to the screen. 


In the "Student_info.cpp" file I had to change two functions:

The "read" function:
1
2
3
4
5
6
7
8
9
void read(Student_info& record, std::vector<std::string>& v)
{
	// read and store the students name and midterm and final exam grades
	record.name = v[0];
	record.midterm = std::stod(v[1]);
	record.final = std::stod(v[2]);

	read_hw(record, v);
}


And the "read_hw" function:
1
2
3
4
5
6
7
8
9
10
void read_hw(Student_info& record, vector<std::string>& hw)
{
	//double x;
	for (size_t lc = 3; lc < hw.size(); lc++)
	{
				//read homework grades
		record.homework.push_back(std::stod(hw[lc]));

	}
}

Just an idea.

Hope that helps,

Andy
Topic archived. No new replies allowed.