I'm creating a program for decompiling some game script files. The latest part I've added is giving me some errors when dealing with dynamic arrays. This is the offending code:
typedef struct _COD9_ANIMREF_1
{
DWORD name;
DWORD reference;
};
typedef struct _COD9_USEANIM_1
{
WORD name; // offset of name
WORD numOfReferences; // reference count
WORD numOfAnimReferences; // reference count
WORD null1; // always null
DWORD* references = NULL; // dynamic array of references, amount = numOfReferences
_COD9_ANIMREF_1* animReferences = NULL; // dynamic array of references, amount = numOfAnimReferences
~_COD9_USEANIM_1()
{
if (references)
delete[] references;
if (animReferences) // program officially breaks here, if continued causes heap corruption
delete[] animReferences;
}
};
typedef struct _COD9_WORK_1
{
_COD9_GSC_1 Hdr;
char* data = NULL;
int* includes = NULL; //done
_COD9_USEANIM_1* usingAnim = NULL; //not done, heap corruption
_COD9_STRING_1* strings = NULL; //done
_COD9_FUNC_1* functions = NULL; //done
_COD9_EXTFUNC_1* extFunctions = NULL; //done
_COD9_RELOC_1* relocations = NULL; //done
~_COD9_WORK_1()
{
if (data)
delete[] data;
if (includes)
delete[] includes;
if (usingAnim)
delete[] usingAnim;
if (strings)
delete[] strings;
if (functions)
delete[] functions;
if (extFunctions)
delete[] extFunctions;
if (relocations)
delete[] relocations;
}
};
if (tstg.Hdr.numOfUsinganimtree)
{
tstg.usingAnim = new _COD9_USEANIM_1[tstg.Hdr.numOfUsinganimtree];
igsc.seekg(tstg.Hdr.usinganimtreeStructs);
for (int i = 0; i < tstg.Hdr.numOfUsinganimtree; i++)
{
_COD9_USEANIM_1 anim;
igsc.read(reinterpret_cast<char*>(&anim.name), sizeof(anim.name));
igsc.read(reinterpret_cast<char*>(&anim.numOfReferences), sizeof(anim.numOfReferences)); // this is 0 in this instance
igsc.read(reinterpret_cast<char*>(&anim.numOfAnimReferences), sizeof(anim.numOfAnimReferences));
igsc.read(reinterpret_cast<char*>(&anim.null1), sizeof(anim.null1));
anim.references = new DWORD[anim.numOfReferences]; // allocate 0 size array so theres something to delete
if(anim.numOfReferences) // should not be entered
{
igsc.read(reinterpret_cast<char*>(&anim.references), (anim.numOfReferences*sizeof(DWORD))); // if numOfReference = 0, function should return
}
anim.animReferences = new _COD9_ANIMREF_1[anim.numOfAnimReferences];
for (int ii = 0; ii < anim.numOfAnimReferences; ii++)
{
_COD9_ANIMREF_1 animref;
igsc.read(reinterpret_cast<char*>(&animref.name), sizeof(animref.name));
igsc.read(reinterpret_cast<char*>(&animref.reference), sizeof(animref.reference));
anim.animReferences[i] = animref;
}
tstg.usingAnim[i] = anim;
printf("anim: %d\n", i); // program reaches this
}
printf("Anims Done\n"); // program doesn't reach this
ReorderUsingAnim(&tstg);
}
Here is what is being read into the fields:
anim.name = 0x06BB
anim.numOfReferences = 0x0000
anim.numOfAnimReferences = 0x0001
anim.null1 = 0x0000
Where I think the error occurs is with the references array, since technically the size is 0 in this instance. But I'm not sure what to do about it, and I'm pretty lost in general about heap corruptions too.
_COD9_USEANIM_1 (why oh why newbies use such horrible names?? Is it enjoyable for them to call variables something like _Z_ASHD532___8AHQ_ ??) has two arrays (why not vectors??), references and anim_references. It has a destructor which frees the arrays if the pointers are not zero. But no constructor. This is DANGEROUS. You should, as a very least, provide a constructor which initializes them references and anim_references to zero. You also need the copy constructor. Remember the rule: if you provide one of the three (default constructor, destructor, copy constructor), you almost certainly need all three.
Ok, now you start your loop
for (int i = 0; i < tstg.Hdr.numOfUsinganimtree; i++)
In the loop you declare the variable anim
_COD9_USEANIM_1 anim;
You allocate its references and animReferences
anim.references = new DWORD[anim.numOfReferences];
...
anim.animReferences = new _COD9_ANIMREF_1[anim.numOfAnimReferences];
Finally you copy it to tstg.usingAnim
tstg.usingAnim[i] = anim;
You know what happens when you copy it? All fields are just copied. So now references and animReferences of tstg.usingAnim[i] point to the same address as references and animReferences of anim.
And then, the block ends. The evil computer calls the destructor for anim. The destructor calls delete[] for anim.references and anim.animReferences. But, references and animReferences of tstg.usingAnim[i] point to the same adresses. In other words, they now point to the array which were deleted.
Now the behaviour of your heap is unpredictable.
The best suggestion: forget arrays, and use vectors. You know, std::vector from the standard library.
Second best suggestion: provide default constructor and copy constructor. (PS: and assignment operator!)
(Note that you program may have other bugs too.)
Related
I have to write a code that gets a string and turns it into an object of a class. Everything is working as expected but I'm unable to deallocate the dynamically allocated 2d array of objects.
I know the issue is within the destructor and the Move assignment operator for the object, I keep getting SIGBRT and EXC_BAD_ACCESS errors when I try to run it.
Below is my Code for the constructor, destructor and move assignment/constructor
//CustomerOrder.cpp
CustomerOrder::CustomerOrder(std::string&
src):Name(src),Product(),ItemCount(),ItemList(),field_width(){
std::vector<ItemInfo> info;
std::string* tokens[] = { &Name, &Product };
Utilities utils;
size_t next_pos = -1;
bool more = true;
for (auto& i : tokens) {
if (!more) break;
*i = utils.extractToken(src, next_pos, more);
}
while (more){
info.push_back(utils.extractToken(src, next_pos, more));
}
if(!info.empty() && info.back().ItemName.empty()){
info.pop_back();
}
ItemCount = info.size();
ItemList = new ItemInfo*[ItemCount];
for (int i = 0; i < ItemCount; i++){
ItemList[i] = new ItemInfo(info.at(i).ItemName);
}
if (utils.getFieldWidth() > field_width){
field_width = utils.getFieldWidth();
}
}
CustomerOrder::~CustomerOrder(){
for(int i = 0; i<ItemCount;i++){
delete[] ItemList[i];
}
delete[] ItemList;
}
CustomerOrder::CustomerOrder(CustomerOrder&& src){
*this = std::move(src);
}
CustomerOrder& CustomerOrder::operator=(CustomerOrder&& src){
if(this!= &src){
delete [] ItemList;
Name = std::move(src.Name);
Product = std::move(src.Product);
ItemCount = std::move(src.ItemCount);
ItemList = std::move(src.ItemList);
src.ItemList = nullptr;
}
return *this;
}
And the ItemInfo struct
//ItemInfo struct
struct ItemInfo
{
std::string ItemName;
unsigned int SerialNumber;
bool FillState;
ItemInfo(std::string src) : ItemName(src), SerialNumber(0),
FillState(false) {};
};
You are combining "new" with "delete[]". If you use "new" use "delete" if you use "new[]" then use "delete[]" for the thing.
This is your problem there: "delete[] ItemList[i];" it should be "delete ItemList[i];" instead
This line of your code ItemList[i] = new ItemInfo(info.at(i).ItemName); doesn't allocate a dynamic array, yet this code in your destructor tries to delete it as thought it was a dynamic array.
for(int i = 0; i<ItemCount;i++){
delete[] ItemList[i];
}
A quick fix would to be to change delete[] to delete. However, it appears as though it would be much easier to simply allocate a single dynamic array. In other words, allocate ItemList as such ItemList = new ItemInfo[ItemCount]; Granted, you would have to change the type, but it makes more sense from what you posted.
Another possible issue is that in your destructor you don't check if the ItemList is a nullptr or actually allocated to anything. To which, your destructor could possibly try to access invalid data. Not only that, but your move operator deletes the ItemList without deleting the data inside of it.
You could make a function to free up the data in ItemList and then call that function from the destructor and move operator.
On a side note, why are you using dynamic 2D arrays when it appears that you know how to use vectors? A vector would handle all of this in a much simpler fashion. For example, the type would be std::vector<std::vector<ItemInfo>>.
I have want to send a struct to json->setInformation but my program crashes when i try to copy the array which is inside the struct. The rest of the data is okay its just the array which makes the crash occur.
info = data->getInformation();
json->setInformation(info);
getInformation returns a struct which i can read in main.cpp
when i try to send this struct to setInformation it crashes...
information.h which holds my struct
struct information{
String location;
String protocol;
uint8_t groupID;
uint8_t* data;
information& operator=(const struct information& that){
location = that.location;
protocol = that.protocol;
groupID = that.groupID;
for (int i = 0; i < 9; ++i){
data[i] = that.data[i];
}
return *this;
}
};
json.cpp
void JSON::setInformation(information data){
info->location = data.location;
info->protocol = data.protocol;
info->groupID = data.groupID;
// for (int i = 0; i < 9; ++i){
// info->data[i] = data.data[i];
// }
// Serial.print("after JSON: ");
// Serial.println(info->data[0]);
}
this code works fine but when i uncomment the for lop which should copy the array it crashes
Did you allocate memory for your uint8_t data* parameter before using it ?
Then remember to deallocate memory when you don't need it anymore, thus avoiding memory leaks.
Your object is passed by copy to the function, but you have no copy constructor.
Default copy constructor will not copy you raw pointer correctly. So either you declare and implement a copy constructor, either you replace your raw pointer (uint8_t*) by a vector (std::vector<uint8_t>) which is safely copyiable (then copying the object will become a valid operation).
Moreover, we can't see who's allocating/deallocating your raw pointer, but I suspect you are missing a destructor function too.
Your code breaks the rule of three which is the minimal requirement for any class you'll declare in C++.
I have made two object of string class each having char* pointer . By shallow copying, i have copied the first object into second object by shallow copying . Now both of them pointing at the same location.
What i have to do is append the char pointer through one object so that it does not make another but increase the size of original char pointer so second object point to the same location.
void String::append(char c) {
auto_ptr<StringBuffer> newdata(new StringBuffer);
newdata.get()->reserve(this->_str->length() + 1);
newdata.get()->smartCopy(this->_str);
this->_str = newdata.release();
this->_str->append(c);
}
The wrapper class of StringBuffer
void StringBuffer::reserve(int n) {
if (_length < n) {
int newlength = n; //max(_length*2,n);
char* newbuf = new char[newlength];
//copy contents of the stored string in the new buffer
revSmartCopy(newbuf);
//return stuff from the new buffer to the stored buffer
delete[] this->_strbuf;
this->_strbuf = newbuf;
this->_length = newlength;
newbuf = 0;
}
}
void StringBuffer::revSmartCopy(char* newString) {
int it = 0;
while (it < this->_length) {
newString[it] = this->_strbuf[it];
it++;
}
}
void StringBuffer::smartCopy(StringBuffer* newString) {
int shorterLength = 0;
(this->_length < newString->_length) ? shorterLength = this->_length : shorterLength = newString->_length;
int it = 0;
while (it < shorterLength) {
*_strbuf++ = *(newString->_strbuf)++;
it++;
}
}
This code is making another copying with object from whom we append pointing to new copy and older one pointing to previous
Let's assume you're doing this as an exercise, because it makes no sense otherwise.
You can't reallocate a pointer to a different size and have it at the same pointer value; this might happen accidentally, but it's impossible to enforce. Since the two objects are independent, the only way to make this work is double indirection - the pointer in your object points to a second pointer, which is the pointer to the character buffer.
You're also going to have a problem with destruction, because you have multiple objects with the same pointer. The standard library has std::shared_ptr to solve this very problem. If a pointer is shared between different objects, use shared_ptr to hold it.
Since there will only be one pointer to the actual character buffer, you can use std::unique_ptr for that one. You could use std::auto_ptr instead, and it will work fine as long as you don't try to copy it, but unique_ptr is a far better choice.
vector<ClassX> xVec;
if (inputFile.peek() == '$')
{
classX classXInstance; //<==================== local instantiation
readFileElements(classXInstance);//<== pass by reference
if(classXInstance.validate())
{
xVec.push_back(classXInstance);///<=============== added here
}
/// destructor of the local copy is called here
}
I get a core dump, tried to debug, however I get so much junk messages with gdb, all I can see that the vector got corrupted, NOT sure if it because the destructor is called is a reason??
EDIT:
my class look like this
class ClassX
{
public:
ClassX() { numberOfX=0; ppXX = NULL; };
~ClassX();
void validate();
char **setX(const vector<string>& Xss);
inline char **getX() {return ppXX;};
private:
int numberOfX;
char **ppXX;
};
and it contains a destructor as follow
ClassX::~ClassX()
{
if (ppXX != NULL)
{
for(int i=0; i < numberOfXX; i++)
{
if (ppXX[i] != NULL)
{
delete [] ppXX[i];
ppXX[i] = NULL;
}
}
// Free array of pointers.
delete [] ppXX;
ppXX = NULL;
}
}
the setX allocate all memory necessary
validate give me a printout of the ppXX[i] and return true if number of elements matches the size of string vector
A copy of classXinstance is stored into xVec, with a pointer ppXX to a region in memory. Now you have two objects pointing to the same region. A moment later, classXinstance is destroyed, so the region is subject to delete. The element within xVec is now pointing to invalid memory.
The best option is to use std::Vector<std::string> instead of char **ppXX; a vector of strings takes care of references and allocation so you don't need to worry about proper construction/copy/destruction.
As someone who never dealt with freeing memory and so on, I got the task to create a dynamic array of struct and create functions to add or delete array elements. When deleting I have to free the memory which is no longer necessary.
when deleting the 2nd element of an array of the size of 3, I move the 3rd element to the 2nd position and then delete the last one. When deleting the last one, I always get an error... Is there anyone who can find an solution for me?
struct myFriend {
myFriend() {
number=0;
hobbys = new char*[10];
}
int number;
char* name;
char** hobbys;
};
int main() {
myFriend* friendList = new myFriend[10];
myFriend* tempFriend = new myFriend;
tempFriend->number=1;
tempFriend->name = "ABC";
myFriend* tempFriend2 = new myFriend;
tempFriend2->number=2;
tempFriend->name = "XYZ";
myFriend* tempFriend3 = new myFriend;
tempFriend3->number=3;
tempFriend3->name = "123";
friendList[0] = *tempFriend;
friendList[1] = *tempFriend2;
friendList[2] = *tempFriend3;
friendList[1] = friendList[2]; //move 3rd element on 2nd position
delete &(friendList[2]); //and delete 3rd element to free memory
}
Why did you create temporary variables? They're not even needed.
If you use std::vector and std::string, the problem you're facing will disappear automatically:
std::vector<myFriend> friendList(10);
friendList[0]->number=1;
friendList[0]->name = "ABC";
friendList[1]->number=2;
friendList[1]->name = "XYZ";
friendList[2]->number=3;
friendList[2]->name = "123";
To make it work, you should redefine your struct as:
struct myFriend {
int number;
std::string name;
std::vector<std::string> hobbys;
};
If you're asked to work with raw pointers, then you should be doing something like this:
struct Friend
{
int number;
char* name;
};
Friend * friends = new Friend[3];
friends[0]->number=1;
friends[0]->name = new char[4];
strcpy(friends[0]->name, "ABC");
//similarly for other : friends[1] and friends[2]
//this is how you should be deleting the allocated memory.
delete [] friends[0]->name;
delete [] friends[1]->name;
delete [] friends[2]->name;
delete [] friends; //and finally this!
And if you do any of the following, it would be wrong, and would invoke undefined behavior:
delete friends[2]; //wrong
delete &(friends[2]); //wrong
It is impossible to delete a subset from array allocated by new []
myFriend* friendList = new myFriend[10];
You have a single whole array
+------------------------------------------------------------------+
| friendList[0] | friendList[1] | ..... | friendList[9] |
+------------------------------------------------------------------+
You can not delete &(friendList[2]).
You get from C++ whole array of 10 elements.
This array starts from friendList (or &(friendList[0])).
operator delete with pointer to the address returned by new (i.e. friendList) is valid
only.
Two things I noticed. (1) You are apparently supposed to "create functions to add or delete elements" but you haven't done that, you have only created one function. (2) You are making your work harder than it needs to be by using a struct that also needs to manage memory. I suggest you use a simpler struct.
Your assignment is, in effect, to make a simple 'vector' class, so I suggest that you do that. Start with a struct that is empty. If the teacher requires you to use the myFriend struct as written, you can add that in after you finish making your vector like functions. I'm going to assume that you aren't allowed to make a class yet because most instructors make the mistake of leaving that until last.
struct MyStruct {
int value; // start with just one value here. Dealing with pointers is more advanced.
};
MyStruct* array;
int size;
int capacity;
void addMyStruct(MyStruct& value); // adds a MyStruct object to the end.
void removeMyStructAtPosition(int position); // removes the MyStruct object that is at 'position'
// I leave the functions for you to implement, it's your homework after all, but I give some clues below.
void addMyStruct(MyStruct& value) {
// First check that there is enough capacity in your array to hold the new value.
// If not, then make a bigger array, and copy all the contents of the old array to the new one.
// (The first time through, you will also have to create the array.)
// Next assign the new value to array[size]; and increment size
}
void removeMyStructAtPosition(int position) {
// If the position is at end (size - 1,) then simply decrement size.
// Otherwise you have to push all the structs one to the left (array[i] = array[i + 1])
// from position to the end of the array.
}
int main() {
// test your new class here.
// don't forget to delete or delete [] any memory that you newed.
}
The array size is fixed at 10, so you don't need to delete any elements from it. But you do need to delete the name and hobbys elements of friendList[1] (and before you overwrite it). There are two problems here:
You are setting friendList[0]->name = "ABC"; Here, "ABC" is a constant zero-terminated string somewhere in memory. You are not allowed to delete it. So you have to make a copy.
You want to delete hobby[i] whenever it was assigned. But in your code, you can't tell whether it was assigned. So you have to set every element to 0 in the constructor, so that you will later know which elements to delete.
The proper place to delete these elements is in myFriends's destructor.
It seems the point of the question is to manage a dynamic array. The main problem is that he is using an array of friendList. Use an array of pointers to friendList:
struct myFriend {
myFriend() {
number=0;
hobbys = new char*[10];
}
int number;
char* name;
char** hobbys;
};
int main() {
myFriend** friendList = new myFriend*[10];
myFriend* tempFriend = new myFriend;
tempFriend->number=1;
tempFriend->name = "ABC";
myFriend* tempFriend2 = new myFriend;
tempFriend2->number=2;
tempFriend->name = "XYZ";
myFriend* tempFriend3 = new myFriend;
tempFriend3->number=3;
tempFriend3->name = "123";
friendList[0] = tempFriend;
friendList[1] = tempFriend2;
friendList[2] = tempFriend3;
friendList[1] = friendList[2]; //move 3rd element on 2nd position
delete friendList[2]; //and delete 3rd element to free memory
}
But everybody else is right -- there are major issues around memory allocation for both 'hobbys' and for 'name' that you need to sort out separately.
To do your homework I'd suggest to learn much more about pointers, new/delete operators, new[]/delete[] operators (not to be confused with new/delete operators) and objects creation/copying/constructors/destructors. It is basic C++ features and your task is all about this.
To point some directions:
1) When you dynamically allocate the object like this
MyType* p = new MyType;
or
MyType* p = new MyType(constructor_parameters);
you get the pointer p to the created object (new allocates memory for a single object of type MyType and calls the constructor of that object).
After your work with that object is finished you have to call
delete p;
delete calls the destructor of the object and then frees memory. If you don't call delete your memory is leaked. If you call it more than once the behavior is undefined (likely heap corruption that may lead to program crash - sometimes at very strange moment).
2) When you dynamically allocate array like this
MyType* p = new MyType[n];
you get the pointer p to the array of n created object located sequentially in memory (new[] allocates single block of memory for n objects of type MyType and calls default constructors for every object).
You cannot change the number of elements in this dynamic array. You can only delete it.
After your work with that array is finished you have to call
delete[] p; // not "delete p;"
delete[] calls the destructor of every object in the array and then frees memory. If you don't call delete[] your memory is leaked. If you call it more than once the behavior is undefined (likely program crash). If you call delete instead of delete[] the behavior is undefined (likely destructor called only for the first object and then attempt to free memory block - but could be anything).
3) When you assign the struct/class then operator= is called. If you have no operator= explicitly defined for your struct/class then implicit operator= is generated (it performs assignment of every non-static member of your struct/class).