so I've been running into a problem where somehow my code is causing segmentation faults before any of my main actually runs. I've never had this happen before and I hardly have a quarter's worth of coding experience so I'm not sure if there's something I'm doing wrong. Everything compiles, at least on my computer, but upon running it my main is never reached.
Context: I'm trying to connect Vertices and Edges in an adjacency matrix and then use Prim's algorithm to build an MST, but that's for later. I built a header file, which originally contained only typdef calls for the structures and the functions. However, I switched the structure definitions to the header file because I was getting memory errors; hence why I think there's an issue with the structs.
graph.h:
//Leland Wong 00000897031
//graph header file
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#ifndef GRAPH_H
#define GRAPH_H
typedef struct vertex
{
double longitude;
double latitude;
char city[30];
int index;
int visited; //0: not visited, 1: visited, 2: visited
struct edge* nexte;
struct vertex* nextv;
double projected;
}VERTEX;
typedef struct edge
{
struct vertex* start;
struct vertex* destination;
double distance;
struct edge* nexte;
}EDGE;
typedef struct graph
{
struct vertex* list[756];
struct edge* matrix[756][756];
}GRAPH;
/*
typedef struct vertex VERTEX;
typedef struct edge EDGE;
typedef struct graph GRAPH;
*/
double findDistance(VERTEX* v1, VERTEX* v2); //compute the distance between two locations
EDGE* connect(VERTEX* v1, VERTEX* v2); //connects two vertices and returns the connecting EDGE
GRAPH primMatrix(GRAPH *g); //connects all vertices using Prim's Algorithm in an adjacency matrix
//void lPrimConnect(VERTEX v); //connects all vertices using Prim's Algorithm in an adjacency list
EDGE* findSmallestEdge(VERTEX v, GRAPH *g); //finds the smallest EDGE connected to v
#endif
graph.c: contains the implementations of all my functions
//functions
//computes the distance between v1 and v2
double findDistance(VERTEX* v1, VERTEX* v2)
{
printf("findDistance");
double long1 = v1->longitude;
double long2 = v2->longitude;
double lat1 = v1->latitude;
double lat2 = v2->latitude;
double distance = 0;
if(long1 < 0)
long1 += 360;
if(long2 < 0)
long2 += 360;
distance = powf((long1-long2), 2) + powf((lat1 - lat2), 2);
distance = sqrt(distance);
return distance;
}
//creates and returns an edge that connects v1 and v2
EDGE* connect(VERTEX* v1, VERTEX* v2)
{
printf("connect");
EDGE *new;
new->start = v1;
new->destination = v2;
new->distance = findDistance(v1, v2);
return new;
}
//finds smallest edge connected to v in GRAPH g
EDGE* findSmallestEdge(VERTEX v, GRAPH *g)
{
printf("findSmallestEdge");
EDGE *tempe;
int i, index;
index = v.index;
//set tempe equal to the first edge connected to v
tempe = g->matrix[index][0];
//find smallest edge connected to v
for(i = 0; i < 756; i++)
{
if(g->matrix[index][i] -> distance < tempe->distance && g->list[index]->visited == 0)
{
tempe = g->matrix[index][i];
}
}
return tempe;
}
//creates an MST out of GRAPH g using Prim's algorithm
GRAPH primMatrix(GRAPH *g)
{
printf("primMatrix");
GRAPH new; // = malloc(sizeof(GRAPH));
EDGE *smallest;
EDGE *tempe;
int i, x;
i = 1;
x = 0;
new.list[0] = g->list[0]; //add root node to MST
g->list[0]->visited = 2;
smallest = findSmallestEdge(*new.list[0], g);
new.matrix[0][smallest->destination->index] = smallest;
//MST will contain all 756 nodes, so run this 755 times to ensure all nodes are reached
while(i < 756)
{
x = 0;
smallest = findSmallestEdge(*new.list[i], g);
//i = number of vertices already reached
while(x < i)
{
tempe = findSmallestEdge(*new.list[x], g);
if(tempe -> distance < smallest -> distance)
{
smallest = tempe;
}
x++;
}
new.list[i] = smallest -> destination;
smallest -> destination -> visited = 2;
new.matrix[smallest->start->index][smallest->destination->index] = smallest;
i++;
}
return new;
}
graphmatrixmain.c: my main function which builds the graphs
#include "graph.h"
int main(int argc, char* argv[])
{
FILE *fp;
static GRAPH g;
char buffer[200];
int i, j;
char city[30];
char *long1;
char *lat1;
if(argc == 1)
{
printf("could not open file\n");
return 0;
}
else
fp = fopen(argv[1], "r");
//read in line of data from txt file, build a new vertex, and insert into list
while(fgets(buffer, 200, fp) != NULL)
{
VERTEX *new = malloc(sizeof(VERTEX));
printf("%s", buffer);
sscanf(buffer, "%s %s %s", city, long1, lat1);
//sscanf(buffer, "%[^\t]\t%[^\t]\t%s", city, long1, lat1);
printf("scanned in data\n");
new->longitude = atof(long1);
new->latitude = atof(lat1);
new->index = i;
g.list[i] = new;
printf("%s: (%lf, %lf)", new->city, new->longitude, new->latitude);
i++;
}
//create EDGE and make connects between every VERTEX in list
for(i = 0; i < 756; i++)
{
for(j = 0; j < 756; j++)
{
g.matrix[i][j] = connect(g.list[i], g.list[j]);
if(j == 0)
{
g.list[i]->nexte = g.matrix[i][j];
}
}
}
return 0;
}
In case its necessary, this is the file i'm reading in from: cities.txt
it contains 756 entries total but as far as the code is concerned size shouldn't be relevant
Shanghai 121.47 31.23
Bombay 72.82 18.96
Karachi 67.01 24.86
Buenos Aires -58.37 -34.61
Delhi 77.21 28.67
Istanbul 29 41.1
Manila 120.97 14.62
Sao Paulo -46.63 -23.53
Moscow 37.62 55.75
I've been running into a problem where somehow my code is causing segmentation faults before any of my main actually runs.
Usually, this means that the data structures that your main tries to place in the automatic storage area overflow the stack. In your situation, it looks like the GRAPH is a suitable suspect to do just that: it has a 2D array with 571536 pointers, which could very well overflow the stack before your main gets a chance to start.
One solution to this problem would be moving the GRAPH into the static area: since you allocate it in the main, it's going to be only one instance of it anyway, so declaring it static should fix the problem:
static GRAPH g;
You might also want to allocate it in the dynamic area using malloc, but in this case it probably does not matter.
Your problem is not "before main" as you state, but in the first few lines of your program. You are not initializing fp, so it could go anywhere. You also have memory errors in your loop with new. You need to copy the value into newly allocated memory.
You cannot see the printfs in your code because the output is buffered and your code crashes before the buffer is flushed. If you put exit(0) just after your printf("error");, you'll see that it works.
I had faced "Segmentation fault occurs before main() execution begins" issue in my C++ code.
Let me try to explain what my issue was and how I could solve it.
A global/static class instance is present in code.
Its constructor is invoked before main() begins.
Inside constructor,in an error handling part, std::cerr is used to display error.
std:cerr was not initialized when constructor was executed.
Even though it says, if #include iostream is there before object is defined then std::cerr is initialized, it was not.
https://en.cppreference.com/w/cpp/io/cerr
If object of std::ios_base::Init is created before constructor, it ensures proper init and deinit of default c++ streams.
why std::cerr was not initialized,
In c++11 objects in iostream are init before other global objects.
But in c++03, it is unspecified.
Use std::ios_base::Init as a private member of class to ensure it is init before stream functions are executed.
Is std::cout guaranteed to be initialized?
One possible reason for segmentation fault before main is the program was mistakenly linked with -shared option. I wasted a lot of time trying to debug it.
Related
I'm trying to find out the degree of connection between 2 entities in a social graph where
1 hop : 1st Degree
2 hop : 2nd Degree
3 hop : 3rd Degree
And so on.
The vertices are the entities and the edges are the friendship between the two entities. Given such a graph I want to analyse the graph and answer the query as to what is the type of connection between the entities.It can be disconnected graph.In case of no connection it'll return 0.
It takes the input as-
Number_of_vertices Number_of_Edges
Edge 1
Edge 2
(So on.)
Query
Output
The degree of connection
Example
Input
5 4
Abhs Krax // Edge 1
Harry Idrina // Edge 2
Harry Jigma // Edge 3
Harry Krax // Edge 4
Abhs Jigma // Query
Output
Degree : 3
I've used BFS to find out the depth between 2 nodes, but my program works only for degree 1. It fails to test the next subsequent member of the queue thus stuck at testing only the 1st member of the queue. What did I miss in my code? The problem is in Connection() function which I couldn't trace.
#include <iostream>
#include <list>
#include <string>
using namespace std;
class Vertex // Each vertex of the graph is represented by the object of the Vertex class
{
public:
// Fields in every vertex node
string name;
std::list<Vertex*> adjacencyList;
bool status;
int depth;
// Constructor which initializes the node
Vertex(string id)
{
name = id;
adjacencyList = list<Vertex*>();
status = false;
depth =0;
}
// Function to add edges by pushing the vertices to its adjacency list
void addEdge(Vertex *v)
{
adjacencyList.push_back(v);
}
};
class Graph{
public:
// Fields of the Graph node
int N;
std::list<Vertex> vertexList;
// Functions to be implemented
int Connection(Vertex,Vertex);
Graph(int n){ // Constructor
N = n;
vertexList = list<Vertex>();
}
/* This function first checks whether the vertex has been already added
to Vertex List of the Graph. If not found it would create the vertex
node and push the node into Vertex List. Then the edges are added by
updating the adjacency list of respective vertices. */
void addEdge(string to, string from ){
if(find(to))
{
Vertex entity_1 = Vertex(to); // New vertex node creation
vertexList.push_back(entity_1); // Pushing to the Vertex List
}
if(find(from))
{
Vertex entity_2 = Vertex(from);
vertexList.push_back(entity_2);
}
Vertex *v1 = &(*(find_it(to)));
Vertex *v2 = &(*(find_it(from)));
v1->addEdge(v2); // Updating respective adjacency list
v2->addEdge(v1);
}
// Function to check whether the vertex is already added in the Vertex List
int find(string check)
{
list<Vertex>::iterator it;
it = find_it(check);
if(it==vertexList.end())
return 1;
else
return 0;
}
// Function which returns pointer to a Vertex in the Vertex List
list<Vertex>::iterator find_it(string check)
{
list<Vertex>::iterator it;
for (it = vertexList.begin(); it != vertexList.end(); it++)
if((check.compare(it->name))==0)
break;
return it;
}
};
int main()
{
int numVertices,numEdges,i,result;
string to,from,queryTo,queryFrom;
cin>>numVertices>>numEdges;
Graph G = Graph(numVertices); // Creating the Graph object
for( i=0;i<numEdges;i++)
{
cin>>to>>from;
G.addEdge(to,from); // Adding Edges to Graph
}
cin>>queryTo>>queryFrom;
// The function you have to write is called here where the address of vertex
// node is passed.
result = G.Connection((*(G.find_it(queryTo))),(*(G.find_it(queryFrom))));
if(!result)
cout<<"No Connection";
else
cout<<"Degree : "<<result;
return 0;
}
int Graph::Connection(Vertex v1,Vertex v2)
{
// Mark all the vertices as not visited
Vertex s=Vertex("xx");
int i=0;
//list<Vertex>::iterator it;
Vertex *temp=&(*(vertexList.begin()));
while(!temp)
temp->status = false,++temp;
// Create a queue for BFS
list<Vertex> queue;
// Mark the current node as visited and enqueue it
v1.status=true;
queue.push_back(v1);
// it will be used to get all adjacent vertices of a vertex
int depth;
while (!queue.empty())
{
depth=0;
// Dequeue a vertex from queue and print it
s = queue.front();
queue.pop_front();
// Get all adjacent vertices of the dequeued vertex s
// If a adjacent has not been visited, then mark it visited
// and enqueue it
temp=s.adjacencyList.front();
while(temp!=NULL)
{
++depth;
// If this adjacent node is the destination node, then return true
if ((v2.name.compare(temp->name))==0)
{
v2.depth=depth;
return v2.depth;
}
// Else, continue to do BFS
if(temp->status==false)
{
temp->status = true;
queue.push_back(*temp);
}
++temp;
}
}
return 0;
}
I'm going to assume that the degree of connection you are trying to compute is the shortest path distance between nodes (with uniform edge costs). You can use Floyd-Warshall to pre-process the graph and answer queries in O(1) time. It's really simple to implement.
int a[N][N]; /* adjacency matrix where N is max node count */
/* build graph here and init distances between distinct nodes to +INFINITY */
/* n is number of nodes */
for(int k = 0; k < n; k++)
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
dist[i][j] = min(dist[i][j], dist[i][k]+dist[k][j]);
To answer a query (x,y) you print dist[x][y].
Your BFS solution looks unnecessarily complicated. Use a vector<in> g[N] to represent your graph. Add an edge x->y with g[x].push_back(y). BFS would look like:
queue<int> Q;
Q.push(s); /* start node */
for(int i = 0; i < n; i++)
{ dist[i] = INFINITY;
}
dist[s] = 0; /* distance to s is set to 0 */
while(Q.empty() == false)
{ int x = Q.front(); Q.pop();
for(int i = 0; i < g[x].size(); i++)
{ int y = g[x][i];
/* process edge x->y */
if(dist[y] == INFINITY)
{ dist[y] = dist[x] + 1;
Q.push(y);
}
}
}
distance between s and any other node t is dist[s][t].
Your code also crashes in a segmentation fault, if you try to find a connection with degree 1. Given your graph try to find "Harry Krax".
I think the mistake is using a pointer Vertex * temp = temp=s.adjacencyList.front(); and later trying to access the next Vertex by ++temp;.
This is not how std::list in combination with pointers work.
If you want to access the next Vertex with ++temp, than you might want to use iterators std::list<x>::iterator temp.
What you are trying to do works with arrays like int a[N], because the elements of an array are adjacent in memory.
With int * aptr = a. ++aptr says that temp is to move to another location in memory that is the size of one int further away.
std::list<x> does not do this. Here the elements can be scatter at different places in memory. (Simplified) it's value and pointers to the previous and next element are stored.
The input is in the following format
5
1 2 9.0
1 3 12.0
2 4 18.0
2 3 6.0
2 5 20.0
3 5 15.0
0
1 5
The first number is the number of vertexes in the graph. Then next lines up to 0 are the edges of the graph. With the first and second numbers being the vertexes and the third being how far the edge is between them. Trying to read in the data and store the edges into there locations in the List adjacency for that vertex. This example would make a graph with five vertexes with edges from 1 to 2&3. 2 to 4&3&1 etc.
I am getting a Segmentation fault after entering four numbers. The fault is happening on line myGraph.vertexInfo[p1].adjacency -> vertex=p2; where is starts trying to store the information. Why am I getting this fault?
#include <cstdio>
using namespace std;
struct ListCell
{
ListCell* next;
int vertex;
double weight;
ListCell(int v, double w, ListCell* nxt)
{
vertex = v;
weight = w;
next = nxt;
}
};
typedef ListCell* List;
struct Vertex
{
bool signaled;
long distance;
List adjacency;
};
struct Graph
{
int numVertices;
Vertex* vertexInfo;
Graph(int n)
{
numVertices = n;
vertexInfo = new Vertex[n+1];
for(int i = 1; i <= n; i++)
{
vertexInfo[i].signaled = false;
}
}
};
//==============================================================
// readIn
//==============================================================
//
//==============================================================
void readIn()
{
int g;
int p1;
int p2;
float edge;
scanf("%i ", &g);
Graph myGraph(g);
scanf("%i", &p1);
while(p1 != 0)
{
scanf("%i", &p2);
scanf("%f", &edge);
myGraph.vertexInfo[p1].adjacency -> vertex=p2;
myGraph.vertexInfo[p2].adjacency -> vertex=p1;
myGraph.vertexInfo[p1].adjacency -> weight=edge;
myGraph.vertexInfo[p2].adjacency -> weight=edge;
scanf("%i", &p1);
}
}
//==============================================================
// main
//==============================================================
int main(int argc, char** argv)
{
readIn();
return 0;
}
You are not following some of the fundamental principles of object construction and intialization for Vertex.
When you construct an instance of Vertex, there is nothing predictable about the values of the member data. I would suggest adding a default constructor that initializes the member data to something sane.
struct Vertex
{
Vertex() : signaled(false), distance(0), adjacency(NULL) {}
bool signaled;
long distance;
List adjacency;
};
Once you do that, the constructor for Graph can be simplified to:
Graph(int n) : numVertices(n)
{
vertexInfo = new Vertex[n+1];
}
Let's look at what adjacency is:
struct Vertex
{
bool signaled;
long distance;
List adjacency;
};
And now let's look at what List is:
typedef ListCell* List;
So, adjacency is actually a pointer to a ListCell. That's fine, we can work with that. But before we can, we must make sure that adjacency actually points to something. The question then becomes what does adjacency point to in your code?
The answer is: who knows? You never make it point to anything, so it's pointing somewhere randomly. And what happens when you try to write to some random area of memory? Well, if you're lucky, you crash.
So, how do you solve this problem? Simple: initialize and manipulate adjacency correctly. You may wish to look into dynamic memory allocation in C++ and the standard library which provides a rather nifty and working implementation of a linked list.
Sidenote: you seem to treat adjacency as a linked list. However, you ought to note that adjacency is a pointer to a node in a linked list. Again, now would a great time to learn about std::list.
The adjacency pointer is uninitialized, so you run into problems when dereferencing it.
You have to initialize your adjacency member:
Graph(int n)
{
numVertices = n;
vertexInfo = new Vertex[n+1];
for(int i = 1; i <= n; i++)
{
vertexInfo[i].signaled = false;
vertexInfo[i].adjacency = new ListCell(i, 0.0, 0);
}
}
I'm having a newbie problem in C++.
I have a struct Edge defined as following:
struct Edge {
int position[4];
int average;
};
Now I need to create a lot of these structs and I created a helper-method for that, that creates an array of these structs based on some parameters:
Edge* createEdges(int some_parameters){
Edge *edges = new Edge[8];
for(int i = 0 ; i < 8; i++){
Edge edge;
edge.position[0] = 1; //fill the rest of the edge array in the same manner
edge.average = 10;
edges[i] = edge;
}
return edges;
}
However when I now call: Edge *edges = createEdges(int some_parameters) there is no sensible data in the Edge array (out of scope?).
I think I'm mixing up some things here but I would prefer if I can make this work without resorting to the vector datastructures. Is this the normal way of handling this or should I declare the edge array myself and pass it to the helper method to fill it up?
Edit:
First of all, I want to thank everyone for there comments/hints/tips/advice/... they have helped me to find the problem that I overlooked so easily.
After I seen the replies that the code should work, I tested the simplified code aswell (something I should have done in the 1st place) and surprisingly, it worked! So then I checked to see why my real code didn't work and the simplified version did.
My real code looked like this:
Edge* createEdges(int some_parameters){
Edge* edges = new Edge[8];
if(some_parameter != 0){
//create the edges as in my 1st snippet
return NULL; //doh, should return edges here !
} else {
return NULL;
}
}
The reason why I had not seen that I simply returned the wrong value (NULL) was because the debugger showed me some 0xf6f6f6 addressess with some negative values for edge.position (something I don't quite understand, it should've just show me 0x000000, perhaps I was just imagining things).
All in all, this was an important lesson in why to never code after 3am, nothing good will come of it!
It's C++ use std::vector:
std::vector<Edge> createEdges(int some_parameters){
std::vector<Edge> edges;
for(int i = 0 ; i < 8; i++){
Edge edge;
edge.position[0] = 1;
edge.average = 10;
edges.push_back(edge);
}
return edges;
}
What you have here works fine. What I feel you are forgetting, is that when you call
Edge *edges = createEdges(int some_parameters)
that edges variable is a pointer to the first element in your list. If you were to then do:
edges[0]
or:
edges[1]
you would see that these are different values, and you need to now loop through to access the array. Just keep in mind that you do not go passed the end of your array during a loop. This is why most people say to use vectors, because you can use more of the safety features with an iterator.
Welcome to C++! The helper-method for creating instances of a class (a struct in C++ is a class where all the fields are public) is called a constructor.
Here is how I would create instances of the edge class, using a constructor.
class Edge {
public:
int position[4];
int average;
Edge();
};
Edge::Edge() //constructor
{
position[0] = 1;//fill the rest of the edge array in the same manner
average = 10;
}
int main()
{
Edge* myEdge = new Edge(); //constructor called
Edge* myEdges[8];
for (int i = 0; i < 8; i++)
{
myEdges[i] = new Edge();
}
return 0;
}
Here is a bit modified version that creates edges in the function, and returns a vector containing all created edges.
#include <iostream>
#include <vector>
struct Edge {
int position[4];
int average;
};
std::vector< Edge > createEdges(int some_parameters){
std::vector< Edge > edges( 8 );
for(int i = 0 ; i < 8; i++){
const Edge edge{ { 1, 0, 0, 0 }, 10 };
edges[i] = edge;
}
return edges;
}
int main()
{
auto e( createEdges(5));
std::cout<<e[0].average<<std::endl;
}
There are few modifications :
Instead of allocating an array of edges, and returning pointer, I return vector
the way edge objects are created, and initialized (I think this is what is asked)
Below is an implementation of Dijkstra's algorithm I wrote from the pseudocode in the Wikipedia article. For a graph with about 40 000 nodes and 80 000 edges, it takes 3 or 4 minutes to run. Is that anything like the right order of magnitude? If not, what's wrong with my implementation?
struct DijkstraVertex {
int index;
vector<int> adj;
vector<double> weights;
double dist;
int prev;
bool opt;
DijkstraVertex(int vertexIndex, vector<int> adjacentVertices, vector<double> edgeWeights) {
index = vertexIndex;
adj = adjacentVertices;
weights = edgeWeights;
dist = numeric_limits<double>::infinity();
prev = -1; // "undefined" node
opt = false; // unoptimized node
}
};
void dijsktra(vector<DijkstraVertex*> graph, int source, vector<double> &dist, vector<int> &prev) {
vector<DijkstraVertex*> Q(G); // set of unoptimized nodes
G[source]->dist = 0;
while (!Q.empty()) {
sort(Q.begin(), Q.end(), dijkstraDistComp); // sort nodes in Q by dist from source
DijkstraVertex* u = Q.front(); // u = node in Q with lowest dist
u->opt = true;
Q.erase(Q.begin());
if (u->dist == numeric_limits<double>::infinity()) {
break; // all remaining vertices are inaccessible from the source
}
for (int i = 0; i < (signed)u->adj.size(); i++) { // for each neighbour of u not in Q
DijkstraVertex* v = G[u->adj[i]];
if (!v->opt) {
double alt = u->dist + u->weights[i];
if (alt < v->dist) {
v->dist = alt;
v->prev = u->index;
}
}
}
}
for (int i = 0; i < (signed)G.size(); i++) {
assert(G[i] != NULL);
dist.push_back(G[i]->dist); // transfer data to dist for output
prev.push_back(G[i]->prev); // transfer data to prev for output
}
}
There are several things you can improve on this:
implementing the priority queue with sort and erase adds a factor of |E| to the runtime - use the heap functions of the STL to get a log(N) insertion and removal into the queue.
do not put all the nodes in the queue at once but only those where you have discovered a path (which may or may not be the optimal, as you can find an indirect path through nodes in the queue).
creating objects for every node creates unneccessary memory fragmentation. If you care about squeezing out the last 5-10%, you could think about a solution to represent the incidence matrix and other information directly as arrays.
Use priority_queue.
My Dijkstra implementation:
struct edge
{
int v,w;
edge(int _w,int _v):w(_w),v(_v){}
};
vector<vector<edge> > g;
enum color {white,gray,black};
vector<int> dijkstra(int s)
{
int n=g.size();
vector<int> d(n,-1);
vector<color> c(n,white);
d[s]=0;
c[s]=gray;
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > q; // declare priority_queue
q.push(make_pair(d[s],s)); //push starting vertex
while(!q.empty())
{
int u=q.top().second;q.pop(); //pop vertex from queue
if(c[u]==black)continue;
c[u]=black;
for(int i=0;i<g[u].size();i++)
{
int v=g[u][i].v,w=g[u][i].w;
if(c[v]==white) //new vertex found
{
d[v]=d[u]+w;
c[v]=gray;
q.push(make_pair(d[v],v)); //add vertex to queue
}
else if(c[v]==gray && d[v]>d[u]+w) //shorter path to gray vertex found
{
d[v]=d[u]+w;
q.push(make_pair(d[v],v)); //push this vertex to queue
}
}
}
return d;
}
Here is the description of my problem:
The Program's Description:
I am implementing a program in C++ that tests Prim's algorithm for finding minimum spanning trees. The objective of the program is calculating the number of seconds it takes to find the minimum spanning tree for a selected number of random graphs.
What i have done up to now?
I finished the implementation of the functions and the header files for the whole program. Since the source code is small, i decided for clarity reasons to paste it with this mail in order to provide a better visualization of the problem.
The Problem:
For some reason, i am facing some sort of "out of range" vector problem during the run time of the application.
The problem is marked in the ("Prim_and_Kruskal_Algorithms.cpp") file.
Requesting help:
I would be really grateful if anyone can help me spotting the problem. I have inlined the source code with this question.
The Source Code:
The (Undirected_Graph.h) file:
#ifndef UNDIRECTED_GRAPH_H
#define UNDIRECTED_GRAPH_H
#include <vector>
using std::vector;
#include <climits>
class Edge;
class Node
{
public:
Node(int); //The constructor.
int id; //For the id of the node.
bool visited; //For checking visited nodes.
int distance;
vector <Edge*> adj; //The adjacent nodes.
};
class Edge
{
public:
Edge(Node*, Node*, int); //The constructor.
Node* start_Node; //The start_Node start of the edge.
Node* end_Node; //The end of the edge.
int w; //The weight of the edge.
bool isConnected(Node* node1, Node* node2) //Checks if the nodes are connected.
{
return((node1 == this->start_Node && node2 == this->end_Node) ||
(node1 == this->end_Node && node2 == this->start_Node));
}
};
class Graph
{
public:
Graph(int); //The Constructor.
int max_Nodes; //Maximum Number of allowed Nodes.
vector <Edge*> edges_List; //For storing the edges of the graph.
vector <Node*> nodes_List; //For storing the nodes of the graph.
void insertEdge(int, int, int);
int getNumNodes();
int getNumEdges();
};
#endif
The (Undirected_Graph.cpp) file:
#include "Undirected_Graph.h"
Node::Node(int id_Num)
{
id = id_Num;
visited = 0;
distance = INT_MAX;
}
Edge::Edge(Node* a, Node* b, int weight)
{
start_Node = a;
end_Node = b;
w = weight;
}
Graph::Graph(int size)
{
max_Nodes = size;
for (int i = 1; i <= max_Nodes; ++i)
{
Node* temp = new Node(i);
nodes_List.push_back(temp);
}
}
void Graph::insertEdge(int x, int y, int w)
{
Node* a = nodes_List[x-1];
Node* b = nodes_List[y-1];
Edge* edge1 = new Edge(a, b, w);
Edge* edge2 = new Edge(b, a, w);
edges_List.push_back(edge1);
a->adj.push_back(edge1);
b->adj.push_back(edge2);
}
int Graph::getNumNodes()
{
return max_Nodes;
}
int Graph::getNumEdges()
{
return edges_List.size();
}
The (Prim_and_Kruskal_Algorithms.h) File:
#ifndef PRIM_AND_KRUSKAL_ALGORITHMS_H
#define PRIM_AND_KRUSKAL_ALGORITHMS_H
class PKA
{
private:
//inline void generateRandomGraph();
protected:
//-No Protected Data Members in this Class.
public:
void runAlgorithms();
void prim();
};
#endif
The (Prim_and_Kruskal_Algorithms.cpp) file
*(The problem is in this file and is marked below):*
#include "Prim_and_Kruskal_Algorithms.h"
#include "Undirected_Graph.h"
#include <iostream>
using std::cout;
using std::cin;
using std::endl;
#include <cstdlib>
using std::rand;
using std::srand;
#include <ctime>
using std::time;
//=============================================================================
//============Global Variables and Settings for the program====================
//=============================================================================
const int numIterations = 1; //How many times the Prim function will run.
const int numNodes = 10; //The number of nodes in each graph.
const int numEdges = 9; //The number of edges for each graph.
const int sRandWeight = 1; //The "start" range of the weight of each edge in the graph.
const int eRandWeight = 100; //The "end" range of the weight of each edge in the graph.
//=============================================================================
//=============================================================================
//=============================================================================
void PKA::runAlgorithms() //Runs the Algorithms
{
srand( time(0) );
cout << "------------------------------" << endl;
//Calling the Functions:
cout << "\nRunning the Prim's Algorithms:\nPlease wait till the completion of the execution time" << endl;
//===============================================
//Start the clock for Prim's Algorithm:
clock_t start, finish;
start = clock();
for(int iter1 = 1; iter1 <= numIterations; ++iter1)
{
prim();
}
//Stop the clock for Prim and print the results:
finish = clock();
cout << "\n\tThe execution time of Prim's Algorithm:\t" << ((double)(finish - start) / CLOCKS_PER_SEC) << " s";
return;
}
void PKA::prim()
{
//=============================================================================
//=============================Generating A Random Graph=======================
//=============================================================================
//Randomizing Values:
//===============================================
int randStartNode = rand() % numNodes; //Generation a random start node.
int randEndNode = rand() % numNodes; //Generating a random end node.
int randWeight; //Random weight for the edge.
while(randEndNode == randStartNode) //Checking if both randomized nodes are equal.
{
randEndNode = (rand() % numNodes);
}
//===============================================
Graph myGraph(numNodes);
for(int i = 0; i < numEdges; ++i)
{
//Generating a random weight:
randWeight = sRandWeight + rand() % eRandWeight;
//Inserting a new Edge:
myGraph.insertEdge(randStartNode, randEndNode, randWeight);
}
//=============================================================================
//=============================================================================
//=============================================================================
int currentNode = 0; //The current Node being under investigation.
int adjCounter = NULL; //How many adjacent nodes do we have for the current node.
int minDistance = NULL;
int minIndex = 0;
myGraph.nodes_List[0]->distance = 0; //Indicate the start node.
myGraph.nodes_List[0]->visited = 1; //The starting node is already considered as a visited node.
for(int i = 0; i < numNodes - 1; i++)
{
//Determine how many adjacent nodes there are for the current node:
adjCounter = myGraph.nodes_List[currentNode]->adj.size();
if(adjCounter == 0) //If there are no adjacent nodes to the current node:
{
myGraph.nodes_List[currentNode]->adj.at(minIndex)->end_Node->visited = 1;
cout << "\n*******Not all nodes are connected!*******" << endl;
continue;
}
minDistance = myGraph.nodes_List[currentNode]->adj.at(0)->w;
minIndex = 0;
for(int counter = 0; adjCounter > 0; adjCounter--, counter++)
{
if(myGraph.nodes_List[currentNode]->adj[counter]->end_Node->visited == false)
{
if(myGraph.nodes_List[currentNode]->distance > myGraph.nodes_List[currentNode]->adj[counter]->w)
{
myGraph.nodes_List[currentNode]->distance = myGraph.nodes_List[currentNode]->adj[counter]->w;
}
if(minDistance > myGraph.nodes_List[currentNode]->adj[counter]->w)
{
minDistance = myGraph.nodes_List[currentNode]->adj[counter]->w;
minIndex = counter;
}
}
}
//======================================================================================
//=========================The Problem is in the following two lines====================
//======================================================================================
//Mark the current node as visited:
myGraph.nodes_List[currentNode]->adj.at(minIndex)->end_Node->visited = 1;
//Switching to the next node that we have just visited:
currentNode = myGraph.nodes_List[currentNode]->adj.at(minIndex)->start_Node->id;
//======================================================================================
//======================================================================================
//======================================================================================
}
}
The (Client_Code.cpp) file: For testing the program.
#include "Prim_and_Kruskal_Algorithms.h"
#include <iostream>
using std::cout;
using std::endl;
int main()
{
cout << "\nWelcome to the Prim and Kruskal Algorithms Comparison!" << endl;
cout << "\nPlease wait until the completion of the algorithms." << endl;
PKA myPKA; //Creating an object of the class.
myPKA.runAlgorithms(); //Running the Algorithm.
cout << "\n\nThe program terminated successfully!" << endl;
return 0;
}
Look at this line:
myGraph.nodes_List[currentNode]->adj.at(minIndex)->end_Node->visited = 1;
As an experienced C++ programmer, I find that line terrifying.
The immediate cause of trouble is that adj doesn't have as many members as you think it does; you're asking for (in my test run) the 5th element of a list of size zero. That sends you off the map, where you then start manipulating memory.
More generally, you are not checking bounds.
More generally still, you should allow these classes to manage their own members. Use accessors and mutators (getX() and setX(...)) so that member access happens all in one place and you can put the bounds checking there. Reaching down myGraph's throat like that is very unsafe.
You'll notice that I haven't said where/when/how the program diverges from intention so that the list doesn't have as many elements as it should. That's because it's too much trouble for me to track it down. If you organize the classes as I suggest, the code will be a lot cleaner, you can check your assumptions in various places, and the bug should become obvious.
EDIT:
To create a random connected graph, try this:
Graph myGraph(numNodes); //Create a new Graph.
// This ensures that the kth node is connected to the [1...(k-1)] subgraph.
for(int k=2 ; k<=numNodes ; ++k)
{
randWeight = rand() % eRandWeight;
myGraph.insertEdge(k, rand()%(k-1)+1, randWeight);
}
// This adds as many extra links as you want.
for(int i = 0; i < numExtraEdges; ++i)
{
randWeight = rand() % eRandWeight;
randStartNode = rand()%(numNodes-1)+1;
randEndNode = rand()%(numNodes-1)+1;
myGraph.insertEdge(randStartNode, randEndNode, randWeight);
}
You have too much code for a casual examination to be sure of anything. But the .at() method will throw the out-of-range exception that you mentioned and that crashing line occurs right after you've updated minIndex so I would suggest reviewing the code that determines that value. Are you using a debugger? What is the value of minIndex at the point of the exception and what is the allowable range?
Also, when you have a monster line of compounded statements like that, it can help in debugging problems like this and give you clearer, simpler looking code if you break it up. Rather than repeating big chunks of code over and over, you can have something like this:
Node * node = myGraph.nodes_List[currentNode];
assert(node);
Edge * minAdjEdge = node->adj.at(minIndex);
assert(minAdjEdge);
Then use minAdjEdge to refer to that edge instead of that repeated compound statement.
It also seems odd to me that your first use of minIndex in the big loop is still using the value determined from the node in the previous iteration, but it's applying it to the new current node. Then you reset it to zero after possibly using the stale value. But that isn't near the line that you say is causing the crash, so that may not be your problem. Like I said, you have a lot of code pasted here so it's hard to follow the entire thing.
It is too much code, but what I can observe at the first glance is that for some reason you are mixing 0-based and 1-based iteration.
Is this intentional? Couldn't that be the cause of your problem?