To be honest, this is an homework assignment, which is about pointers and dynamic memory allocation. This program simulates the manipulation of file directories. Since it contains several files, so I would just paste a few parts here.The program crashed when I executed the function for the third time. I have looked up some solutions for debugging this kind of program crash, but still can't fix it.
struct fs_node
{
char* name;
fs_node* parent_directory;
fs_node** content;
int no_of_content;
};
bool loop_for_md (fs_node* current_directory, const char* dir_name)
{
//current_directory is initialized in the main.cpp
//find out whether the content contains the same name as dir_name
if(current_directory->content==NULL)
{
return true;
}
else
{
for(int i = 0; i<= current_directory->no_of_content; i++)
{
if(strcmp(current_directory->content[i]->name, dir_name)==0)
return false;
else
continue;
}
}
return true;
}
bool make_dir (fs_node* current_directory, const char* dir_name)
{
if(current_directory->content==NULL)
{
fs_node** n = new fs_node*[20];
current_directory->content = n;
fs_node *x = new fs_node();
current_directory->content[current_directory->no_of_content]=x;
x->parent_directory = current_directory;
x->name = new char[100];
strcpy(x->name, dir_name);
current_directory->no_of_content++;
delete x;
x=0;
}
else if(loop_for_md(current_directory, dir_name))//I expect that this part crashes
{
fs_node* x = new fs_node();
current_directory->content[current_directory->no_of_content]=x;
x->parent_directory = current_directory;
x->name = new char[100];
strcpy(x->name, dir_name);
current_directory->no_of_content++;
delete x;
x=0;
}
else return false;
return true;
}
When you have created a new fs_node and inserted it into the directory tree, you shouldn't delete it - that ends the object's lifetime and you're not allowed to use it after that.
Formally, doing so has "undefined behaviour", which means that anything can happen, including a crash much later in a different piece of code or - much worse - appearing to work as intended.
Related
I am practicing memory allocation and disk management with C++. I just all of the work.. it just looks and seem's a little too easy. I am not sure if my pointer and my allocation and deallocations are correct. My Total FreeSpace looks like it will work, but it looks too basic. I just need someone's programming experience. When I try to run this code it gives me some kind of Error.
Bug Error
Please DO NOT ADD any new Global Variable.
const int MMSIZE = 60136;
char MM[MMIZE];
//** Initialize set up any data needed to manage the memory
void initializeMemory(void)
{
//**increments through the POOL_SIZE
for (int a = 0; a < MMSIZE; a++) {
MM[a] = 'NULL';
}
}
// return a pointer inside the memory
// If no chunk can accommodate aSize call onOutOfMemory()
void* allocate(int size)
{
//******NOT SURE*******
int *p = new int;
*p = 5;
return ((void*) 0);
}
// Free up a chunk previously allocated
void deallocate(void* mPointer)
{
//******NOT SURE*******
int *p = new int;
delete p;
p = 0;
p = new int(10);
}
//Scan the memory and return the total free space remaining
int remaining(void)
{
//******NOT SURE*******
int free = 0;
for (int a = 0; a < MMSIZE; a++)
{
if (MM[a] < MMSIZE)
{
free += a;
}
}
int free2 = free - MMSIZE;
return free2;
}
This code looks unfinished for even a sample but
//** Initialize set up any data needed to manage the memory
void initializeMemory(void)
{
//**increments through the POOL_SIZE
for (int a = 0; a < MMSIZE; a++) {
MM[a] = 'NULL';// <=== this should not even compile as the single quote should only take one character like '\x0' or 'N'
}
}
should not even compile as the single quote should only take one character like '\x0' or 'N'
but post the complete module and i can help you more and maybe explain a few things.
Without discussing other aspects of your code (such as memory leaking etc), the specific error you are getting most likely comes from *int_pointer = 0xDEADBEEF; line. int_pointer is equal to 0, because int_pointer = (long *)allocate(sizeof(long)); and your void* allocate(int size) with its return ((void*) 0); always returns 0. So you are getting exactly that exception: attempting to write 0xDEADBEEF at address 0x00000000, which is a forbidden operation (there is some OS specific stuff at low addresses).
I have a small example program here for the particle photon that has a memory bug that I cannot figure out.
What it does: loads up a buffer with small string chunks, converts that large buffer back into a string. Then it creates a bunch of objects that are only wrappers for small chunks of buffer. It does this repetitively, and I don't allocate any new memory after the setup(), yet the memory goes down slowly until it crashes.
main.cpp
includes, variable declarations
#include "application.h" //needed when compiling spark locally
#include <string>
#include <unordered_map>
#include "dummyclass.h"
using namespace std;
SYSTEM_MODE(MANUAL);
char* buffer;
unordered_map<int, DummyClass*> store;
string alphabet;
unsigned char alphabet_range;
unsigned char state;
int num_chars;
static const unsigned char STATE_INIT = 0;
static const unsigned char STATE_LOAD_BUFFER = 1;
static const unsigned char STATE_PREP_FOR_DESERIALIZE = 2;
static const unsigned char STATE_FAKE_DESERIALIZE = 3;
static const unsigned char STATE_FINISH_RESTART = 4;
delete objects helper function
bool delete_objects()
{
Serial.println("deleting objects in 'store'");
for(auto iter = store.begin(); iter != store.end(); iter++)
{
delete iter->second;
iter->second = nullptr;
}
store.clear();
if(store.empty())
return true;
else
return false;
}
set up function, allocates memory, initial assignments
void setup()
{
Serial.begin(9600);
Serial1.begin(38400);
delay(2000);
buffer = new char[9000];
alphabet = string("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789~!##$^&*()_-?/><[]{}|");
alphabet_range = alphabet.length() - 1;
state = STATE_INIT;
num_chars = 0;
}
loop function, gets run over and over
void loop()
{
switch(state){
case STATE_INIT: {
strcpy(buffer, "");
state = STATE_LOAD_BUFFER;
delay(1000);
break;
}
case STATE_LOAD_BUFFER: {
if(num_chars < 6000){
string chunk;
for(char i = 0; i < 200; i++){
int index = rand() % alphabet_range;
chunk.append(alphabet.substr(index, 1));
num_chars++;
}
strcat(buffer, chunk.c_str());
}
else{
num_chars = 0;
state = STATE_PREP_FOR_DESERIALIZE;
}
delay(500);
break;
}
case STATE_PREP_FOR_DESERIALIZE: {
Serial.println("\nAttempting to delete current object set...");
delay(500);
if(delete_objects())
Serial.println("_delete_objects succeeded");
else {
Serial.println("_delete_objects failed");
break;
}
state = STATE_FAKE_DESERIALIZE;
delay(1000);
break;
}
case STATE_FAKE_DESERIALIZE: {
string buff_string(buffer);
if(buff_string.length() == 0){
Serial.println("Main:: EMPTY STRING CONVERTED FROM BUFFER");
}
int index = 0;
int key = 1;
while(index < buff_string.length())
{
int amount = (rand() % 50) + 5;
DummyClass* dcp = new DummyClass(buff_string.substr(index, amount));
store[key] = dcp;
index += amount;
key++;
}
state = STATE_FINISH_RESTART;
delay(1000);
break;
}
case STATE_FINISH_RESTART: {
state = STATE_INIT;
break;
}
}
}
dummyclass.h
very minimal, constructor just stores a string in a character buffer. this object is just a wrapper.
using namespace std;
class DummyClass {
private:
char* _container;
public:
DummyClass(){
}
DummyClass(string input){
_container = new char[input.length()];
strcpy(_container, input.c_str());
}
~DummyClass(){
delete _container;
_container = nullptr;
}
char* ShowMeWhatYouGot(){
return _container;
}
};
EDIT:
This is a real problem that I am having, I'm not sure why it is getting downvoted. Help me out here, how can I be more clear? I'm reluctant to shrink the code since it imitates many aspects of a much bigger program that it is modeling simply. I want to keep the structure of the code in place in case this bug is an emergent property.
Always account for the string terminator:
DummyClass(string input){
_container = new char[input.length()];
strcpy(_container, input.c_str());
}
Allocates one too few bytes to hold the input string and terminator that is then copied into it. The \0that's appended at the end is overwriting something, which is most likely metadata required to re-integrate the alloced memory fragment back into the heap successfully. I'm actually surprised it didn't crash...
It probably doesn't happen every allocation (only when you overflow into a new 8 byte aligned chunk), but once is enough :)
So, after some testing, I'd like to give a shout out to Russ Schultz who commented the right answer. If you want to post a solution formally, I would be happy to mark it as correct.
The memory bug is caused by allocating the char buffer _container without considering the null terminating character, meaning I am loading in a string that is too big. (not entirely sure why this causes a bug and doesn't throw an error?)
On a different site however, I also received this piece of advice:
string chunk;
for(char i = 0; i < 200; i++){
int index = rand() % alphabet_range;
chunk.append(alphabet.substr(index, 1));
// strcat(buffer, alphabet.substring(index, index + 1));
num_chars++;
}
This loop looks suspect to me. You are depending on the string append method to grow chunk as needed, but you know you are going to run that loop 200 times. Why not use the string reserve method to just allocate that much space? I bet that this chews up a lot of memory with each new char you append calling realloc, potentially fragmenting memory.
This ended up not being the solution, but it might be good to know.
In my class, I've got - inter alia - a pointer:
Class GSM
{
//...
private:
char *Pin;
//...
}
My constructor initialize it as:
GSM::GSM()
{
//...
Pin = NULL;
//...
}
Now, I want to set default value ("1234") to my PIN. I tried very simple way:
bool GSM::setDefaultValue()
{
lock();
Pin = "0";
for (uint8 i =0; i < 4; ++i)
{
Pin[i] = i+1;
}
unlock();
return true;
}
But it didn't work. When I run my program (I use Visual Studio 2010) there is an error:
Access violation writing location 0x005011d8
I tried to remove line
Pin = "0";
But it didn't help. I have to initialize it as NULL in constructor. It's part of a larger project, but I think, the code above is what makes me trouble. I'm still pretty new in C++/OOP and sometimes I still get confused by pointers.
What should I do to improve my code and the way I think?
EDIT: As requested, I have to add that I can't use std::string. I'm trainee at company, project is pretty big (like thousands of files) and I did not see any std here and I'm not allowed to use it.
You need to give the Pin some memory. Something like this:
Pin = new char[5]; // To make space for terminating `\0`;
for(...)
{
Pin[i] = '0' + i + 1;
}
Pin[4] = '\0'; // End of the string so we can use it as a string.
...
You should then use delete [] Pin; somewhere too (Typically in the destructor of the class, but depending on how it's used, it may be needed elsewhere, such as assignment operator, and you need to also write a copy-constructor, see Rule Of Three).
In proper C++, you should use std::string instead, and you could then do:
Class GSM
{
//...
private:
std::string Pin;
....
Pin = "0000";
for (uint8 i =0; i < 4; ++i)
{
Pin[i] += i+1;
}
Using std::string avoids most of the problems of allocating/deallocating memory, and "just works" when you copy, assign or destroy the class - because the std::string implementation and the compiler does the work for you.
You need to allocate a block of memory to store "1234". This memory block will be pointed by your Pin pointer.
You can try:
bool GSM::setDefaultValue()
{
lock();
Pin = new char[4];
for (uint8 i =0; i < 4; ++i)
{
Pin[i] = '0' + (i + 1);
}
unlock();
return true;
}
As you have allocated dynamicaly a memory block, you should always release it when you don't need it anymore. To do so, you should add a destructor to your class:
GSM::~GSM()
{
delete [] Pin;
}
Simple answer:
Instead of using the heap (new delete) just allocate space in your class for the four character pin:
Class GSM
{
//...
private:
char Pin[5];
//...
}
The length is fixed at 5 (to allow space for 4 characters and terminating null ('\0'), but as long as you only need to store a maximum of 4 characters, you are fine.
Of course, if you want to make it easy to change in the future:
Class GSM
{
//...
private:
const int pin_length = 4;
char Pin[pin_length+1];
//...
}
Your function to set the value will then look like:
bool GSM::setDefaultValue()
{
lock();
for (char i = 0; i < pin_length; ++i)
{
Pin[i] = i+1;
}
Pin[pin_length]=0;
unlock();
return true;
}
or even:
bool GSM::setDefaultValue()
{
lock();
strcpy(Pin,"1234"); //though you would have to change this if the pin-length changes.
unlock();
return true;
}
My program has been written using classes from the SDL library.
I have the following class:
class s_group
{
private:
SDL_Surface* image;
unsigned int* F_total;
float* F_length;
SDL_Rect** F;
float* F_current;
unsigned int S_total;
unsigned int S_current;
public:
s_group(void);
virtual ~s_group(void);
bool setup( const char* filename, unsigned int s );
//other member functions
};
Private member pointers each store the location of memory declared on the heap, as allocated by the member function setup.
bool s_group::setup( const char* filename, unsigned int s )
{
s_group::~s_group();//delete already allocated heap memory
if(!load_file(image, filename))
{
image = NULL;
return false;
}
S_total = s;
F = new SDL_Rect*[S_total];
F_total = new unsigned int[S_total];
F_length = new float[S_total];
F_current = new float[S_total];
for(unsigned int index = 0; index < S_total; ++index)
{
F[index] = NULL;
F_total[index] = 0;
F_length[index] = 0.f;
F_current[index] = 0.f;
}
//loop for each array slot and set values of data
return true;
}
Within a large function I create an object of this class on the heap, storing its address in an s_group pointer named sparkle.
s_group* sparkle = new s_group;
sparkle->setup("sparkle_final.png", 1 );
On completion of the function I call delete to reallocate the heap memory. Removing this line solves the problem, however there would then be a memory leak.
delete sparkle;
sparkle = NULL;
This will call the destructor of the class which is where I believe the error occurs due to an internal use of the delete operator.
s_group::~s_group(void)
{
SDL_FreeSurface(image);
image = NULL;
for(unsigned int s = 0; s < S_total; ++s)
{
for(unsigned int f = 0; f < F_total[s]; ++f)
{
F[s][f].x = 0;
F[s][f].y = 0;
F[s][f].w = 0;
F[s][f].h = 0;
}
delete[] F[s];
F[s] = NULL;
}
delete[] F;
F = NULL;
delete[] F_total;
F_total = NULL;
delete[] F_length;
F_length = NULL;
delete[] F_current;
F_current = NULL;
S_total = 0;
S_current = 0;
}
On reaching the delete operator, a dialog box appears stating:
Windows has triggered a breakpoint in Program.exe. This may be due to a corruption of the heap, which indicates a bug in Program.exe or any of the DLLs it has loaded.
How do I delete this object without causing the heap corruption?
From effective C++ Scott Meyers
Item 9: Never call virtual functions during construction or destruction.
You shouldn't call virtual functions during construction or destruction, because the calls won't do what you think, and if they did, you'd still be unhappy. If you're a recovering Java or C# programmer, pay close attention to this Item, because this is a place where those languages zig, while C++ zags.
Actually, even though you should define your destructor, calling it forcibly should be out of the question
I'm unable to compile your code but here goes..
The first thing I noticed was that you called your destructor.. You don't want to do that! Instead, create a release function and call that.
The next thing I noticed is that there is no FRAME variable within the class itself.. so this line:
FRAME = new SDL_Rect*[S_total];
is going to cause a compilation error and your destructor uses FRAME but no such variable exists. I think you meant to change it to F because if not, then this line:
F[index] = NULL;
is undefined behaviour since F is uninitialized..
Also, you never initialized each index of FRAME and so accessing it in the destructor like:
FRAME[s][f].x = 0;
is a no-no.
Again, you call
delete[] F;
F = NULL;
but F has no memory allocated and is uninitialized.
Thus with all the patches I think:
class s_group
{
private:
SDL_Surface* image;
unsigned int* F_total;
float* F_length;
SDL_Rect** FRAME;
float* F_current;
unsigned int S_total;
unsigned int S_current;
void Release();
public:
s_group(void);
virtual ~s_group(void);
bool setup(const char* filename, unsigned int s);
//other member functions
};
bool s_group::setup(const char* filename, unsigned int s)
{
Release();//delete already allocated heap memory
if(!load_file(image, filename))
{
image = NULL;
return false;
}
S_total = s;
FRAME = new SDL_Rect*[S_total];
F_total = new unsigned int[S_total];
F_length = new float[S_total];
F_current = new float[S_total];
for(unsigned int index = 0; index < S_total; ++index)
{
FRAME[index] = NULL;
F_total[index] = 0;
F_length[index] = 0.f;
F_current[index] = 0.f;
}
//loop for each array slot and set values of data
return true;
}
void s_group::Release()
{
SDL_FreeSurface(image);
image = NULL;
for(unsigned int s = 0; s < S_total; ++s)
{
for(unsigned int f = 0; f < F_total[s]; ++f)
{
if (FRAME[s])
{
FRAME[s][f].x = 0;
FRAME[s][f].y = 0;
FRAME[s][f].w = 0;
FRAME[s][f].h = 0;
}
}
delete[] FRAME[s];
FRAME[s] = NULL;
}
delete[] FRAME;
FRAME = NULL;
delete[] F_total;
F_total = NULL;
delete[] F_length;
F_length = NULL;
delete[] F_current;
F_current = NULL;
S_total = 0;
S_current = 0;
}
s_group::~s_group(void)
{
Release();
}
should do it.. Just don't forget to allocate memory for FRAME[index] I wasn't sure how much or what you wanted to allocate so I changed the Release function to check if FRAME[index] is valid with an if-statement
I would strongly advise that you use some SmartPointers and forget about handling every single memory allocation yourself..
Since posting this question I have located the source of the error and solved the issue.
In a separate section of code which set the data values for the dynamic 2D array the loop validation was incorrect.
for( unsigned int index = 0; index <= F_total[ S_current ]; ++index ) {
//set data values for each slot in the array
F[ S_current ][ index ].x = 0; etc...
}
As can be seen the loop will clearly attempt to modify a location equal to the size of the created array. Noting of course that arrays begin at index 0, so the final slot will be at size - 1. Something very silly that I missed when writing the code. Actual loop:
for( unsigned int index = 0; index < F_total[ S_current ]; ++index ) {
//set data values for each slot in the array
F[ S_current ][ index ].x = 0; etc...
}
A message for anyone attempting their own memory management:
Finding the source of heap corruption is difficult as the compiler will locate the error in sections of code which do not necessarily cause the problem.
The cause of the problem will only ever be in the section of your code which is affecting the memory. Ensure that you do not attempt to access or worse modify any memory that you have not been given.
I still believe that memory management is a great way to learn and would rather complete any projects in this way than using containers or smart pointers as recommended. This is my personal preference despite custom memory management often offering very few advantages, only complexities.
When asking for assistance provide all related code on the problem. Although the compiler may direct you to the problem in one section, as I said before, with heap corruption it's not necessarily there.
Basically, I'm passing a pointer to a character string into my constructor, which in turn initializes its base constructor when passing the string value in. For some reason strlen() is not working, so it does not go into the right if statement. I have checked to make sure that there is a value in the variable and there is.
Here is my code, I've taken out all the irrelevant parts:
Label class contents:
Label(int row, int column, const char *s, int length = 0) : LField(row, column, length, s, false)
{
}
Label (const Label &obj) : LField(obj)\
{
}
~Label()
{
}
Field *clone() const
{
return new Label(*this);
}
LField class contents:
LField(int rowNumVal, int colNumVal, int widthVal, const char *valVal = "", bool canEditVal = true)
{
if(strlen(valVal) > 0)
{
}
else
{
//This is where it jumps to, even though the value in
//valVal is 'SFields:'
val = NULL;
}
}
Field *clone() const
{
return new LField(*this);
}
LField(const LField &clone) {
delete[] val;
val = new char[strlen(clone.val) + 1];
strcpy(val, clone.val);
rowNum = clone.rowNum;
colNum = clone.colNum;
width = clone.width;
canEdit = clone.canEdit;
index = clone.index;
}
Screen class contents:
class Screen {
Field *fields[50];
int numOfFields;
int currentField;
public:
Screen()
{
numOfFields = 0;
currentField = 0;
for(int i = 0; i < 50; i++)
fields[i] = NULL;
}
~Screen()
{
for (int i = 0; i < 50; i++)
delete[] fields[i];
}
int add(const Field &obj)
{
int returnVal = 0;
if (currentField < 50)
{
delete[] fields[currentField];
fields[currentField] = obj.clone();
numOfFields += 1;
currentField += 1;
returnVal = numOfFields;
}
return returnVal;
}
Screen& operator+=(const Field &obj)
{
int temp = 0;
temp = add(obj);
return *this;
}
};
Main:
int main () {
Screen s1;
s1 += Label(3, 3, "SFields:");
}
Hopefully someone is able to see if I am doing something wrong.
<LANGUAGE FEATURE XXXX IS BROKEN>! ... No, it isn't.
Just before measuring the string, write in a puts(valVal), to ensure you are not mistaken about the contents of that variable.
Marcin at this point the problem will come down to debugging, I copied your code with some minor omissions and got the correct result.
Now it needs to be said, you should be using more C++ idiomatic code. For instance you should be using std::string instead of const char* and std::vector instead of your raw arrays.
Here is an example of what the LField constructor would look like with std::string:
#include <string> // header for string
LField(int rowNumVal,
int colNumVal,
int widthVal,
const std::string& valVal = "",
bool canEditVal = true)
{
std::cout << valVal;
if(valVal.length() > 0)
{
}
else
{
//This is where it jumps to, even though the value in
//valVal is 'SFields:'
//val = NULL;
}
}
Using these types will make your life considerably easier and if you make the change it may just fix your problem too.
PREVIOUS:
So you can be CERTAIN that the string is not being passed in correctly add a printline just before the strlen call. Once you do this work backward with printlines until you find where the string is not being set. This is a basic debugging technique.
Label(int row,
int column,
const char *s,
int length = 0) :
LField(row, column, length, s, false) {
}
LField(int rowNumVal,
int colNumVal,
int widthVal,
const char *valVal = "",
bool canEditVal = true)
{
std::cout << valVal << std::endl;
if(strlen(valVal) > 0)
{
}
else {
//This is where it jumps to, even though the value in
//valVal is 'SFields:'
val = NULL;
}
}
Whenever there is strange behavior like this, memory getting screwed up is almost always the culprit. Never mix new with delete[] OR new[] with delete. The latter is slightly worse than the former but they are both bad news. delete[] should only be used in conjunction with new[]. Mixing these allocation/deallocation notations will result in undefined behavior. Since you are never using new[], replace all of your delete[] calls with delete. It is also good practice and good manners to set your pointers to NULL after you delete them. It is highly unlikely that you will be the only one debugging this code and it would be extremely annoying to debug your pointers thinking that there is valid memory there, when in fact there isn't.
Mixing these notations inevitably lead to exploits and memory leaks.
There is a problem here:
LField(const LField &clone) {
delete[] val;
val = new char[strlen(clone.val) + 1];
val is uninitialized when the constructor is called, and you are deleting it.