I am trying to program a qt widget application which deals with linked list of nodes, the node contains 3 char* data member and 2 int members and a "next" pointer of "node" type,
my issue is the char* members in nodes of the linked list are getting saved same as the third char* member, I tried using debugger and found out that
the length of all the 3 ints i.e. lentitl,lenpub,lenpub are initialized to same value, and all the 3 char* members are getting same value,
the node constructor is as follows
node::node(char* titl,char* auth,char* pub,int pri,int stockp)
{
int lentitl,lenauth,lenpub;
lenpub=strlen(pub);
lentitl=strlen(titl);
lenauth=strlen(auth);
title=new char[lentitl+1];
author=new char[lenauth+1];
publisher=new char[lenpub+1];
strcpy(title,titl);
strcpy(author,auth);
strcpy(publisher,pub);
price=pri;
stockposition=stockp;
next=NULL;
}
the node functions if called from another class function named "addbook",and addbook is called from mainwindow.cpp, the functions calls for addbook is as follows
void MainWindow::on_addbook_clicked()
{
char *titl,*auth,*pub;
int pri,stockp;
titl=ui->title->toPlainText().toLatin1().data();
auth=ui->author->toPlainText().toLatin1().data();
pub=ui->publisher->toPlainText().toLatin1().data();
pri=ui->price->toPlainText().toInt();
stockp=ui->stockposition->toPlainText().toInt();
p.addbook(titl,auth,pub,pri,stockp);
}
and the function call for node is as follows
void shop::addbook( char *titl, char *auth, char *pub, int pri, int stockp)
{
node *p=new node(titl,auth,pub,pri,stockp);
if(start==NULL)
{
start=p;
end=p;
}
else
{
p->next=start;
start=p;
}
}
the zip of whole project is here
and here is the link to the ouput
the screen shot of the output is
as you can see in the image,string entered at "publisher" textedit is getting set to all 3 char* of the node,
can anyone explain me why is this happening?
titl=ui->title->toPlainText().toLatin1().data();
This is probably the issue: toLatin1() returns a new QByteArray which owns its data. You assign the internal data pointer of that QByteArray to titl. However, the QByteArray is only a temporary variable and will get destroyed in the next line of code. When the QByteArray gets destroyed, it will free its data, which means your titl is now pointing to memory that was already freed - i.e. titl points to an invalid memory location.
Suggested solution alternatives:
Use QString instead of char* in your node class, much easier to deal with memory management issues that way
Make sure your QByteArray temporary lives as long as you need the data (until you strcpy the data):
QByteArray titl = ui->title->toPlainText().toLatin1();
QByteArray auth = ui->author->toPlainText().toLatin1();
QByteArray pub = ui->publisher->toPlainText().toLatin1();
pri=ui->price->toPlainText().toInt();
stockp=ui->stockposition->toPlainText().toInt();
p.addbook(titl.data(),auth.data(),pub.data(),pri,stockp);
I'm not expert of Qt but there is something that doesn't sound good to me in terms of C++. Mainly a function in your code is returning a "char * " pointer instead of a "const char*", I'm talking about these lines:
char *titl,*auth,*pub;
int pri,stockp;
titl=ui->title->toPlainText().toLatin1().data();
If the data was directly provided by the title class I expect to obtain a const char* pointer in order to do not be allowed to modify it. The only reason you can handle a char* ( or in other words a well known library is designed to return a non const pointer) is that there is a temporary object or a static buffer in the middle of the calls: toLatin1 or data.
Reading the docs of Qt: toLatin1 is returning a temporary object, a QByteArray.
QByteArray toLatin1 () const
for more information: http://qt-project.org/doc/qt-4.8/qstring.html
So simply modify each string request like:
QByteArray titlArray = ui->title->toPlainText().toLatin1();
titl=titlArray.data();
in that way each string will point to a still existing buffer when you are calling
p.addbook(titl,auth,pub,pri,stockp);
Related
I am new to CPP and I am writing a program as an assignment to simulate a train path system that includes destinations and starts using object oriented programming .
I have 2 classes as shown below (there is a a passenger class but it is not relevant ) :
class Train
{
public:
int cooldown_time;
int travel_time;
int time_since_movement;
int id;
class Station *start;
class Station *destination;
vector<Passenger *> current_passengers;
string status;
void add_train(vector<string> commands, vector<Station> stations, vector<Train> &trains)
{
travel_time = stoi(commands[THIRD_PART + 1]);
cooldown_time = stoi(commands[THIRD_PART + 2]);
status = TSTATUS1;
start = station_search(stations, commands[SECOND_PART]); // this is where the problem happens
destination = station_search(stations, commands[THIRD_PART]);
id = stations.size();
}
};
class Station
{
public:
int tuffy_price;
string city_name;
vector<Passenger *> current_passengers;
vector<Train *> current_trains;
int id;
void add_station(vector<Station> &stations, vector<string> &commands)
{
tuffy_price = stoi(commands[THIRD_PART]);
city_name = commands[SECOND_PART];
id = stations.size();
}
};
I have a search function dedicated to finding the start and destination based off a command that user enters for example :the user enters "add_train cityname1 cityname2 <cooldown_time> <travel_time>". my program detects the city names and searches a vector I have named stations with a key that is the city name and returns a pointer (because of the complications in memory behavior in a function , i set it to pointer) to that station-object .
the function is as below :
Station *station_search(vector<Station> stations, string key)
{
Station *dummy;
for (int i = 0; i < stations.size(); i++)
{
if (stations[i].city_name == key)
{
return &stations[i];
}
}
return dummy;
}}
my problem is with my search function's weird behavior , when I debug the program I see the function find the correct station object and return a pointer to it ,but when the execution returns to the constructor function it randomly (maybe not randomly ) turns the first pointer relating to the start station to null and replaces the values inside with garbage ones.
but after the function searches for the destination station it does not do this and the execution is correct.
Could someone explain why this error is occurring?
My guess is that I have not understood local variables and pointer returns well enough and I have committed a rookie mistake somewhere but I don't seem to find it .
PS: I did not include the full code as it's too long I can include it by attaching a file ,comment down if it's necessary.
Station *station_search(vector<Station> stations, string key)
If you take a closer look here, you will see that the stations parameter is passed by value, which means that after this function returns, this stations parameters will get destroyed. It will be no more. It will cease to exist. It will become an ex-parameter.
However this station_search returns a pointer to some value in this vector. Therefore, rules of logic dictate that it will return a pointer to a destroyed object. Attempting to dereference that pointer, in any way, becomes undefined behavior.
Your other class methods receive parameters by reference, so you must already understand the difference between passing parameters by value vs. by reference, so you should simply do the same here.
Here you are passing a copy of the vector, which is destroyed when the function returns. Additionally, if the key is not found an uninitialized pointer is returned.
Station *station_search(vector<Station> stations, string key)
{
for (Station &station : stations)
{
if (stations.city_name == key)
{
// Pointer becomes invalid when you leave.
// Accessing this pointer will cause undefined behavior.
return &station;
}
}
// This would always cause undefined behavior as dummy was not initialized.
return nullptr;
}
You should pass in a reference and initialize dummy:
Station *station_search(vector<Station> &stations, string key)
I have been trying to fix a potentially sketchy use of void* casting in a task scheduling application for weeks now. Note that I am NOT getting a compiler error, but the scheduling program crashes after a few hours (for some unknown reason). Consider the following code snippets from the program:
In main:
CString* buffer = new CString(temp);
parameters.set("jobID", (void *) buffer);
runJob(parameters);
Also, VGridTaskParam class is as follows:
class VGridTaskParam{
map<CString, void *> p; // maps from CString to a pointer that is not known
public:
void * get(CString name){
return p[name]; // returns the map value of the name which is an unknown pointer
}
void set(CString name, void * data){
p[name] = data; // sets the mpa value given a particular key
}
};
Snippet of some work in runJob(VGridTaskParam parameters) function is:
void runJob(VGridTaskParam parameters)
{
CString JIDstr; // job ID string
//get job ID as CString
CString* pJID = (CString*)parameters.get("jobID");
JIDstr = CString(*pJID);
delete pJID; ******************************
}
Some questions: does the last delete line (marked with several asterisks) delete the memory allocation created in the main program? Is my use of void* casting warranted in this situation. Note that whenever I run a job, I spawn a new thread. Can someone suggest a potential fix to this problem? What should I look at to fix this problem?
does the last delete line (marked with several asterisks) delete the memory allocation created in the main program?
Yes.
Is my use of void* casting warranted in this situation?
No, that is superfluous. Any pointer can be cast to void* without the explicit cast.
Can someone suggest a potential fix to this problem? What should I look at to fix this problem?
I can't suggest a fix without an MCVE.
A few possible issues:
what will happen if you write twice to same key? This will cause a memory leak.
there are few possible solutions, like:
void set(CString name, void * data){
if(p[name]!=NULL)
delete p[name];
p[name] = data; // sets the mpa value given a particular key
}
Another thing: Are you running multi-threaded or single threaded? for multi-threaded you should synchronize the get and set methods to prevent pointer changes in a middle of action.
And finally, what happen if key not found? you should check the returned pointer. also remove from the set or null it after usage.
void runJob(VGridTaskParam parameters)
{
CString JIDstr; // job ID string
//get job ID as CString
CString* pJID = (CString*)parameters.get("jobID");
if(pJID){
parameters.set("jobID",NULL);
JIDstr = CString(*pJID);
delete pJID; ******************************
}
}
I see two things that looks a bit surprising to me.
1) runJob takes parameters of type VGridTaskParam by value. Maybe you want a reference instead. That can't explain the crash, though.
2) You never seem to remove anything from the map even though you delete the memory associated with the pointer. So there is a risk that you'll use the pointer value later on. Either as a dereference or as a double delete.
3) You don't implement any check for the key being present in the map.
Your delete is correct. It deletes an allocated block of memory of the size of the pointer's type at the pointer's address. So when you allocate a CString with new you will delete the exact same amount of memory.
The hang up is when you delete unallocated memory. There are several ways I could see this happening in your current code:
runJob requests an key not in the map (this will return a default initialized CString* as the newly created value.)
You call runJob on a previously deleted item in the map (this value would already be deallocated and deleting it again would be illegal.)
We can't see all your code but if there is the potential to delete these pointers elsewhere there could also be a double cleanup problem there.
You should code your map more defensively, for example:
const void* get(const CString& name) const {
return p.find(name) == p.cend() ? nullptr : p[name]; // returns the map value of the name which is an unknown pointer
}
void set(const CString& name, void* data) {
void* toOverwrite = get(name);
if(toOverwrite != nullptr) {
delete toOverwrite;
}
p[name] = data; // sets the mpa value given a particular key
}
void remove(const CString& name) {
if(p.find(name) != p.end()) {
p.erase(name);
}
}
In your function you'd need to change to testing if the return of get was a nullptr before operating on the result, and rather than delete you'd need to call remove. This keeps all modification of p local to the class, thereby guaranteeing correct maintenance of the map.
Will the following code work? -
void doSomething(char* in)
{
strcpy(in,"mytext");
}
Here's how the function is being called:
doSomething(testIn);
OtherFn(testIn);
The char* in is used in other places in the code... and we are passing it by value to the function doSomething. I understand when we pass by value, a copy of the string stored in char* is copied within the function. So, when we do a strcpy, will it copy to the local copy or to the char* in that was passed in as an argument?
My understanding is we need to do: doSomething(char* &in). Is that right?
When you want to modify just the contents of what the pointer points to, use:
doSomething(char* in)
So, yes,
void doSomething(char* in)
{
strcpy(in,"mytext");
}
will work just fine as long as in points to enough memory to hold "mytest" and a terminating null character.
There are times when you want to modify where the pointer points to, for example, by allocating new memory. Then, you need to pass a reference to a pointer.
void doSomething(char*& in)
{
in = new char[200];
strcpy(in,"mytext");
}
and use it as:
char* s = NULL;
doSomething(s);
// Now s points to memory that was allocated in doSomething.
// Use s
// make sure to deallocate the memory.
delete [] s;
I'm having trouble passing data by reference to a given method - when the method access the data, it's corrupted, but I'm sure that when the method is called it's not (by debuggin). Here is something like the code:
//Calling code
const void* tempPointer = array.mid(readerPos,RE8K_ICDEF_HARMONICS_SIZE).constData();
const re8k_ics_harmonics* const newStruct = static_cast< const re8k_ics_harmonics* >(tempPointer);
DSInstance().updateHarmonics(*newStruct);
//method
void DataStream::updateHarmonics(const re8k_ics_harmonics &infoHarmonics, ...)
{
//Use infoHarmonics
}
So if I use the debugger and go put a breakpoint in the "calling code" in the last line and watch what is in newStruct, I see that the data is perfect as it should be. Than the method is called and I put a new breakpoint (or go "next line") till enter the first line inside updateHarmonics, and when I see the content of infoHarmonics, I see part of the data corrupted and part of it is there.
Why is the code becoming corrupted? What should I do? :x
Additional info:
array is a Qt's QByteArray
readerPos is a int that iterates over the QByteArray from the point data should be read
constData() returns a const char*
Thanks,
Momergil
QByteArray QByteArray::mid (int pos, int len = -1 ) const
This function returns object by value, so in the line
const void* tempPointer = array.mid(readerPos,
RE8K_ICDEF_HARMONICS_SIZE).constData();
you are taking a pointer to temporary data. This pointer is not valid just in the next line. You should create object on the heap or use stack allocated instance, e.g:
QByteArray midQ = array.mid(readerPos, RE8K_ICDEF_HARMONICS_SIZE);
const void* tempPointer = midQ.constData(); // pointer valid as long
// as midQ breaths
const re8k_ics_harmonics* const newStruct =
static_cast< const re8k_ics_harmonics* >(tempPointer);
DSInstance().updateHarmonics(*newStruct);
First of all, everything is happening in an if{} statement in a do{}while loop. I have a struct that contains some const char pointers. I'm trying to get info into a temp struct with new string values each iteration, then push this struct into a vector of said structs, so that when the function exits, the vector is populated with different struct objects.
do{
if()
{
sound_device_t newDevice; //<--- Why is this the same mem address each iteration?
//I thought it would be destroyed when it's scope was (the if block)
const char * newPath;
someFunction(&newPath); //puts a string into newPath
newDevice.firstString = newPath; //<-- This works.
QString otherPath(const char *);
//...some QString manipulation...//
newDevice.secondString = otherPath.toLocal8Bit().data(); //<--this doesn't
vector_of_structs -> push_back(newDevice);
}
}while (...)
I was under the impression that push_back copied the argument struct's values into its own version. Why is the QString giving me problems? I am using QString because it has some good string manipulation functions (i.e. insert and section), but I'll exchange it if I need to for something that works.
I have also tried putting the QString's data into a char * and then strcpy'ing it into the struct, but that has the same result. Every iteration rewrites newDevice.secondString.
QByteArray::data() is only valid so long as the ByteArray is unchanged. Destroying the temporary is changing.
In other words after the semi colon of the line newDevice.secondString = otherPath.toLocal8Bit().data(); the QByteArray returned by toLocal8Bit is destroyed and the stored array deleted.
There are a couple of issues with your code:
if statement without condition (!)
invalid construction: QString otherPath(const char *); You probably want an "otherPath" variable there similarly to "newPath".
You are mixing qt types with std containers. You should take a loo at QStringList.
Needless pointer usage: newDevice.secondString = otherPath.toLocal8Bit().data();
The last one is especially critical since you destroy otherPath before the next iteration. The solution is to use a deep copy in there.
I would write something like this:
do {
if(cond) {
sound_device_t newDevice;
const char * newPath;
someFunction(&newPath);
newDevice.firstString = newPath;
// Get other path
QString otherPath(otherPath);
//...some QString manipulation...
newDevice.secondQString = otherPath;
// or: strcpy( newDevice.secondString, otherPath.toLocal8Bit().data());
vector_of_structs->push_back(newDevice);
}
} while (...)
That being said, depending on what you are trying to do, QtMultiMedia might be better used overall for your sound device purpose. As long as dbus goes, there is also a QtDBus add-on module.
Thanks for all the help guys. I got the original code working with just one tweak:
newDevice.secondString = otherPath.toLocal8Bit().data();
should be changed to
newDevice.secondString = strdup(otherPath.toLocal8Bit().data());
This does the buffer allocation directly, as #ratchet freak was suggesting. strcpy() doesn't work because it still connects newDevice.secondString with the QByteArray, just like toLatin1().data() does.