I'm doing a Data Structures exercise and I have been blocked since yesterday with a bus error, which I reckon is because I'm doing bad things with the memory. But I cannot figure out what exactly.
These are the requirements that I have established for the practice:
able to add a product (any way will do) to the list
able to retrieve the product in the list at the current position (next, prev, moveToStart, moveToEnd… there's cursor pointer, called "actual" here)
any changes I do to the retrieved product should be updated in the data structure (ie. list::retrieve(*product), product->visits++)
This is the code that I have. Apologies about the var names, I have to do it in spanish and therefore names are in spanish.
class producto { // My product
public:
string marca;
double precio;
int visitas;
int compras;
producto () {}
producto (string M, double P, int V = 0, int C = 0) : marca(M), precio(P), visitas(V), compras(C) {}
};
class nodo {
public:
producto valor; // value
nodo *siguiente; // next
nodo *anterior; // prev
nodo (producto P, nodo *A = NULL, nodo *S = NULL) : valor(P), anterior(A), siguiente(S) {}
};
class lista {
private:
nodo *inicio;
nodo *final;
nodo *actual;
public:
lista();
bool esta_vacia(); // is empty?
bool es_final(); // is the end?
int insertar(producto p); // insert given p
void moverPrincipio(); // "move to beginning"
void siguiente(); // "next"
void imprimir(); // "print"
int leer(producto *p); // read, return 0 or 1 if successful, return product by ref
};
lista::lista() {
this->inicio = NULL;
this->final = NULL;
this->actual = NULL;
}
bool lista::esta_vacia() {
return (this->inicio == NULL);
}
bool lista::es_final() {
return (this->actual == NULL);
}
void lista::moverPrincipio() {
this->actual = this->inicio;
}
void lista::siguiente() {
if(!this->es_final()) {
this->actual = this->actual->siguiente;
}
}
void lista::imprimir() {
int i = 1;
producto *p;
this->moverPrincipio();
while(!this->es_final()) {
if(this->leer(p) == 0) {
cout << i << ".- ##" << p->marca << "##, Views ##" << p->visitas << "##\n";
p->visitas++;
i++;
this->siguiente();
}
}
}
int lista::leer(producto *p) {
if(this->actual != NULL) {
*p = this->actual->valor;
return 0;
} else {
return 1;
}
}
int lista::insertar(producto p) {
if(this->esta_vacia()) {
nodo *tmp = new nodo(p);
this->inicio = tmp;
this->final = this->inicio;
} else {
nodo *tmp = new nodo(p, this->final);
this->final->siguiente = tmp;
this->final = tmp;
}
return 0;
}
I have removed unnecessary code. This is how I'm using it (and failing miserably):
lista *productos = new lista();
productos->insertar(producto("Shoes", 19.90));
productos->insertar(producto("Socks", 25.00));
// I should expect views = 0
productos->imprimir();
// But now, views = 1
productos->imprimir();
Upon execution, the only thing I get is "Bus error: 10" when doing imprimir ("print"), the first time. Insertion works without errors (but something could be wrong there too).
My idea is to hold the product inside the node, and give a reference to its location when returning it, so that any changes are reflected there too (for example, increase the view or purchase counter of a retrieved element, reflects the change when reading the list later).
I'd be extremely thankful if someone could point out the mistakes I'm doing here.
Thanks!!
UPDATE Here's a compilable example.
You pass a pointer to lista::leer and you want to write a value to it. You will be writing in unallocated memory. Probably, what you wanted was a pointer to the actual element.
First of all, you need to modify the signature:
int lista::leer(producto **p);
note the double star, since we will be writing the pointer itself.
Then, you have to assign a pointer to actual->valor to it in lista::leer:
*p = &(this->actual->valor);
Finally, you have to pass a pointer to p in lista::imprimir:
if(this->leer(&p) == 0) {
// ...
}
Alternatively, you might modify lista::leer to return a pointer and check if it is nullptr/NULL.
Related
I'm trying to create an object of a class called Cell and store it in a linked list. I'm sure I could do this with an array, but part of my assignment is that I use a linked list and I didn't think I'd get this many problems. This is currently my node. Right now, I have all these variables stored in the node, but I'd rather create an object(Called "Cell") to store them. Info should be a pointer to an object of type T. Right now, that T should be of type Cell.
template<class T>
struct Node {
T *info;
Node<T> *nodeP;
Node<T> *linkP;
int nodeNumber = 0;
bool purchased = false;
std::string color = " ";
int index = 0;
int max_num = 0;
std::string name = " ";
int price;
};
In here I am creating the node and adding it to a linked list. At the moment I'm just filling in values of the node, but I'm trying to create an object of type Cell and assign it's address to the pointer info. I've tried a couple different ways but keep coming back with errors. I commented them out so you can see what I've tried.
template<class T>
void Board<T>::setCellValue() {
//open file
ifstream inFile;
string line;
inFile.open("CellValues.txt");
//Check for Error
if (inFile.fail()) {
cerr << "File does not exist!";
exit(1);
}
int index = 0, max_num = 0, count = 0, price = 0;
string color, name;
istringstream inStream;
while (getline(inFile, line)) {
inStream.clear();
inStream.str(line);
inStream >> color >> index >> max_num >> name >> price;
//creates node
Node<T> *newNodeP = new Node<T>;
//create pointer, assign pointer to pointer in Node
//Cell<T> *cellPtr = new Cell<T>(count, name, color, index, max_num, price);
//newNode->info= cellPtr;
//creating anonymous object and assigning to the node? I think
newNodeP->info = new Cell<T>(color, index, max_num, name, price);
//weird way I was just experimenting with
newNodeP->info->Cell<T>(count, name, color, index, max_num, price);
//fills node values(this is what I want to handle in the object
newNodeP->color = color;
newNodeP->index = index;
newNodeP->max_num = max_num;
newNodeP->name = name;
newNodeP->nodeNumber += count;
newNodeP->price = price;
newNodeP->linkP = NULL;
if (firstP != NULL)
lastP->linkP = newNodeP;
else
firstP = newNodeP;
lastP = newNodeP;
count++;
}
}
Currently, I have two ways of returning the node landed on. One returns a Node* and sort of works. It returns the pointer to the node, and I can access the values inside that node, but I can't figure out how to store the pointer to that node.
//Find Cell
template<class T>
Node<T>* Board<T>::findCell(int id) {
for (Node<T> *traverseP = firstP; traverseP != NULL; traverseP = traverseP->linkP) {
if (traverseP->nodeNumber == id) {
return traverseP;
}
}
return nullptr;
}
//how I call it in main. it returns an address to that node, but I'm getting errors trying to store that address in a pointer.
cout << "You landed on cell " << gameBoard.findCell(player.getCellNum()) << endl << endl;
Node<T> *ptr = gameboard.findCell(player.getCellNum())->info;
This second way, I think returns the reference to the object in the node, but my earlier problem is stopping me from figuring that out.
//Return Cell
template <class T>
T Board<T>::returnCell(int id) {
for (Node<T> *traverseP = firstP; traverseP != NULL; traverseP = traverseP->linkP) {
if (traverseP->nodeNumber == id) {
return traverseP->info;
}
}
return nullptr;
}
//How i'm calling it in main. I don't really know what it's returning though because it only prints "You landed on " and then nothing else.
cout << "You landed on " << gameBoard.returnCell(player.getCellNum()) << endl;
I have to implement a queue from the scratch for an assignment without using any premade lib. It's working fine when I Enqueue e Dequeue, but when I Dequeue a unary queue (with just one element) and I print it, doesn't show that it is empty, the Print function actually print blank spaces, and when try to Enqueue new elements after Dequeuing the whole list it simply does not push new elements. Looks like that the reference to the first element got lost. If someone could help, here is the code:
structs:
typedef struct{
int number;
}TItem;
typedef struct cell{
struct cell *pNext;
TItem item;
}TCell;
typedef struct{
TCell *pFirst;
TCell *pLast;
}TQueue;
And the imlementations:
void Init(TQueue* pQueue){
pQueue->pFirst = new TCell;
pQueue->pLast = pQueue->pFirst;
pQueue->pFirst->pNext = NULL;
}
int Is_Empty(TQueue* pQueue){
return (pQueue->pFirst == pQueue->pLast);
}
int Enqueue(TQueue* pQueue, TItem x){
pQueue->pLast->pNext = new TCell;
pQueue->pLast = pQueue->pLast->pNext;
pQueue->pLast->item = x;
pQueue->pLast->pNext = NULL;
return 1;
}
int Dequeue(TQueue* pQueue, TItem* pX){
if(Is_Empty(pQueue))
return 0;
TCell* aux;
aux = pQueue->pFirst->pNext;
*pX = aux->item;
pQueue->pFirst->pNext = aux->pNext;
delete aux;
return 1;
}
void Print(TQueue* pQueue){
if(Is_Empty(pQueue) == 1)
cout << "EMPTY"<<endl;
TCell *temp;
temp = pQueue->pFirst->pNext;
cout << "Queue:"<<endl;
while( temp != NULL){
cout << temp->item.number << " ";
temp = temp->pNext;
}
}
PS: The memory allocation for the queue is made on the main block
Problem
Your Dequeue logic is invalid. All you need to do when dequeing is remove the first item in the queue.
Solution
int Dequeue(TQueue* pQueue, TItem* pX){
if(Is_Empty(pQueue))
return 0;
TCell* aux;
//aux = pQueue->pFirst->pNext;
aux = pQueue->pFirst;
//*pX = aux->item;
//pQueue->pFirst->pNext = aux->pNext;
pQueue->pFirst = aux->pNext;
delete aux;
return 1;
}
Also I don't know why you need to pass TItem* pX as you don't really use it.
So I am new to c++ sorry if this is not to clear.
I have a class:
class Item
{
int noItem;
int qItem;
public:
Item(int noItem, int qItem)
{
this->noItem = noItem;
this->qItem = qItem;
}
int getNoItem()
{
return noItem;
}
int getQntItem()
{
return qItem;
}
};
Then the following class:
class Element
{
public:
Element()
{
data = NULL;
}
//to set and access data hold in node
void setElement(Item *data)
{
this->data = data;
}
Item* getElement(void)
{
return(data);
}
private:
Item *data;
};
This one also:
class ListeChainee
{
public:
ListeChainee()
{
courant = NULL;
}
void ajoutListe(Item *data)
{
Element *newData;
//set data
newData->setElement(data);
//check if list is empty
if( courant == NULL)
{
//set current pointer
courant = newData;
}
}
//get data from element pointed at by current pointer
Item* elementCourant(void)
{
if(courant != NULL)
{
return courant->getElement();
}
else
{
return NULL;
}
}
private:
//data members
Element *courant; //pointer to current element in list
};
The code is missing some stuff for other things, but my problem is this:
int main(int argc, char* argv[])
{
ListeChainee listeCH;
Item i1(123,456);
listeCH.ajoutListe(&i1);
cout << listeCH.elementCourant()->getNoItem();
system("pause");
return 0;
}
I expect 123 to be outputted, but I see some other number. Not sure why.
Thanks.
Your Element *newData doesn't have an instance of Element class, so it will crash when you try to access the instance pointed by newData.
Try to change Element *newData; to Element *newData = new Element;.
ps.: Don't forget to delete it when you don't need the instance any more.
This method is writing to uninitialized memory:
void ajoutListe(Item *data)
{
Element *new;
//set data
new->setElement(data); // Right here, "new" is an uninitialized pointer
//check if list is empty
if( courant == NULL)
{
//set current pointer
courant = new;
}
}
I'm surprised this compiles (does it?). This code should also crash.
The strange number you're getting is surely some random part of memory. You may want to think more about memory management -- there are numerous problems here. When ajoutListe is called, why does the courant member only get set if it's NULL? Do we just leak the new Element? How do we actually traverse this list?
I want to make an implementation of stack, I found a working model on the internet, unfortunately it is based on the idea that I know the size of the stack I want to implement right away. What I want to do is be able to add segments to my stack as they are needed, because potential maximum amount of the slots required goes into 10s of thousands and from my understanding making the size set in stone (when all of it is not needed most of the time) is a huge waste of memory and loss of the execution speed of the program. I also do not want to use any complex prewritten functions in my implementation (the functions provided by STL or different libraries such as vector etc.) as I want to understand all of them more by trying to make them myself/with brief help.
struct variabl {
char *given_name;
double value;
};
variabl* variables[50000];
int c = 0;
int end_of_stack = 0;
class Stack
{
private:
int top, length;
char *z;
int index_struc = 0;
public:
Stack(int = 0);
~Stack();
char pop();
void push();
};
Stack::Stack(int size) /*
This is where the problem begins, I want to be able to allocate the size
dynamically.
*/
{
top = -1;
length = size;
z = new char[length];
}
void Stack::push()
{
++top;
z[top] = variables[index_struc]->value;
index_struc++;
}
char Stack::pop()
{
end_of_stack = 0;
if (z == 0 || top == -1)
{
end_of_stack = 1;
return NULL;
}
char top_stack = z[top];
top--;
length--;
return top_stack;
}
Stack::~Stack()
{
delete[] z;
}
I had somewhat of a idea, and tried doing
Stack stackk
//whenever I want to put another thing into stack
stackk.push = new char;
but then I didnt completely understand how will it work for my purpose, I don't think it will be fully accessible with the pop method etc because it will be a set of separate arrays/variables right? I want the implementation to remain reasonably simple so I can understand it.
Change your push function to take a parameter, rather than needing to reference variables.
To handle pushes, start with an initial length of your array z (and change z to a better variable name). When you are pushing a new value, check if the new value will mean that the size of your array is too small (by comparing length and top). If it will exceed the current size, allocate a bigger array and copy the values from z to the new array, free up z, and make z point to the new array.
Here you have a simple implementation without the need of reallocating arrays. It uses the auxiliary class Node, that holds a value, and a pointer to another Node (that is set to NULL to indicate the end of the stack).
main() tests the stack by reading commands of the form
p c: push c to the stack
g: print top of stack and pop
#include <cstdlib>
#include <iostream>
using namespace std;
class Node {
private:
char c;
Node *next;
public:
Node(char cc, Node *nnext){
c = cc;
next = nnext;
}
char getChar(){
return c;
}
Node *getNext(){
return next;
}
~Node(){}
};
class Stack {
private:
Node *start;
public:
Stack(){
start = NULL;
}
void push(char c){
start = new Node(c, start);
}
char pop(){
if(start == NULL){
//Handle error
cerr << "pop on empty stack" << endl;
exit(1);
}
else {
char r = (*start).getChar();
Node* newstart = (*start).getNext();
delete start;
start = newstart;
return r;
}
}
bool empty(){
return start == NULL;
}
};
int main(){
char c, k;
Stack st;
while(cin>>c){
switch(c){
case 'p':
cin >> k;
st.push(k);
break;
case 'g':
cout << st.pop()<<endl;
break;
}
}
return 0;
}
I'm getting a segfault when I run this code and I'm not sure why. Commenting out a particular line (marked below) removes the segfault, which led me to believe that the recursive use of the iterator "i" may have been causing trouble, but even after changing it to a pointer I get a segfault.
void executeCommands(string inputstream, linklist<linklist<transform> > trsMetastack)
{
int * i=new int;
(*i) = 0;
while((*i)<inputstream.length())
{
string command = getCommand((*i),inputstream);
string cmd = getArguments(command,0);
//cout << getArguments(command,0) << " " << endl;
if (cmd=="translate")
{
transform trs;
trs.type=1;
trs.arguments[0]=getValue(getArguments(command,2));
trs.arguments[1]=getValue(getArguments(command,3));
((trsMetastack.top)->value).push(trs);
executeCommands(getArguments(command,1),trsMetastack);
}
if (cmd=="group")
{
//make a NEW TRANSFORMS STACK, set CURRENT stack to that one
linklist<transform> transformStack;
trsMetastack.push(transformStack);
//cout << "|" << getAllArguments(command) << "|" << endl;
executeCommands(getAllArguments(command),trsMetastack); // COMMENTING THIS LINE OUT removes the segfault
}
if (cmd=="line")
{ //POP transforms off of the whole stack/metastack conglomeration and apply them.
while ((trsMetastack.isEmpty())==0)
{
while ((((trsMetastack.top)->value).isEmpty())==0) //this pops a single _stack_ in the metastack
{ transform tBA = ((trsMetastack.top)->value).pop();
cout << tBA.type << tBA.arguments[0] << tBA.arguments[1];
}
trsMetastack.pop();
}
}
"Metastack" is a linked list of linked lists that I have to send to the function during recursion, declared as such:
linklist<transform> transformStack;
linklist<linklist<transform> > trsMetastack;
trsMetastack.push(transformStack);
executeCommands(stdinstring,trsMetastack);
The "Getallarguments" function is just meant to extract a majority of a string given it, like so:
string getAllArguments(string expr) // Gets the whole string of arguments
{
expr = expr.replace(0,1," ");
int space = expr.find_first_of(" ",1);
return expr.substr(space+1,expr.length()-space-1);
}
And here is the linked list class definition.
template <class dataclass>
struct linkm {
dataclass value; //transform object, point object, string... you name it
linkm *next;
};
template <class dataclass>
class linklist
{
public:
linklist()
{top = NULL;}
~linklist()
{}
void push(dataclass num)
{
cout << "pushed";
linkm<dataclass> *temp = new linkm<dataclass>;
temp->value = num;
temp->next = top;
top = temp;
}
dataclass pop()
{
cout << "pop"<< endl;
//if (top == NULL) {return dataclass obj;}
linkm<dataclass> * temp;
temp = top;
dataclass value;
value = temp->value;
top = temp->next;
delete temp;
return value;
}
bool isEmpty()
{
if (top == NULL)
return 1;
return 0;
}
// private:
linkm<dataclass> *top;
};
Thanks for taking the time to read this. I know the problem is vague but I just spent the last hour trying to debug this with gdb, I honestly dunno what it could be.
It could be anything, but my wild guess is, ironically: stack overflow.
You might want to try passing your data structures around as references, e.g.:
void executeCommands(string &inputstream, linklist<linklist<transform> > &trsMetastack)
But as Vlad has pointed out, you might want to get familiar with gdb.