简体   繁体   中英

c++ memory access violation

I am learning C++ and I am having quite a lot of trouble with my current assignment. I have completed a good amount of it so far. However I have been making very slow progress of late due to what I think is my poor understanding of what is going on behind the scenes.

What I am trying to do in the following code is:

  • Get two separate values (Bullet damage). Done.
  • Create a dynamic array. Done.
  • Fill a part (that is the size of a modulus of a random number between 1 and 10) of said dynamic array with one value and the rest with the other in a random order. Here I am having trouble.
  • Clean up the memory used by said dynamic array. Done.

The error I get is as follows:

Unhandled exception at 0x00a323e3 in Class 3.exe: 0xC0000005: Access violation reading location 0xcdcdcdcd.

I'm pretty sure that the error occurs when I try to set ammoArray[i] to a value. But I don't know why it's giving it to me, my code compiles fine. I played around with it a bit and in one case I got it to store the memory addresses of bDamage and sDamage and then print out the memory addresses of each element of the array. What I want it to do is store the values held by bDamage and sDamage .

Now for my question:

Why won't ammoArray store the values of bDamage and sDamage instead of the memory addresses of the array's elements? And How do I get it to store them?

Here is my Main.cpp:

#include <cstdlib>
#include "Ammunition.h"
#include "AmmunitionManager.h"
#include "Bullet.h"
#include "Game.h"
#include "Pistol.h"
#include "Player.h"
#include "Point.h"
#include "Shell.h"
#include "Shotgun.h"
#include "WeaponManager.h"
#include "Weapons.h"

using namespace std;

void main()
{
    Ammunition amVar;
    AmmunitionManager *var = new AmmunitionManager();

    amVar.setBDamage(6);
    amVar.setSDamage(2);
    var->FillAmmoArray(amVar.getSDamage(),amVar.getBDamage());

    system("PAUSE");
}

Here is the .h file of the class in question:

#ifndef AMMUNITIONMANAGER_H
#define AMMUNITIONMANAGER_H
#include "Point.h"
#include "Ammunition.h"

class AmmunitionManager
{
public:
    AmmunitionManager();
    AmmunitionManager(int,int);
    ~AmmunitionManager();

    void FillAmmoArray(int,int);
private:
    Ammunition Ammo;
    int **ammoArray;
};

#endif

Here is the .cpp file of the class in question:

#include <iostream>
#include <cstdlib>
#include <ctime>
#include "AmmunitionManager.h"
#include "Point.h"
#include "Ammunition.h"

using namespace std;

AmmunitionManager::AmmunitionManager()
{
}


AmmunitionManager::AmmunitionManager(int sDamage,int bDamage)
    :Ammo(sDamage,bDamage)
{
    cout << "Filling ammo reservoir." << endl;
    ammoArray = new int* [10];
}

void AmmunitionManager::FillAmmoArray(int sDamage,int bDamage)
{
    srand(time(NULL));
    int *holdS = &sDamage;
    int *holdB = &bDamage;
    if(ammoArray)
    {
        for(int i = 0;i < 9;i++)
        {
            int randC = rand() % 2 + 1;
            if(randC == 1)
            {
                cout << "Was: " << ammoArray[i] << endl;//I am getting the error here.
                ammoArray[i] = holdS;
                cout << "Is: " << ammoArray[i] << endl;
            }
            if(randC == 2)
            {
                cout << "Was: " << ammoArray[i] << endl;//I am getting the error here.
                ammoArray[i] = holdB;
                cout << "Is: " << ammoArray[i] << endl;
            }
        }
    }
}

AmmunitionManager::~AmmunitionManager()
{
    *ammoArray = 0;
    if(ammoArray)
    {
        delete [] ammoArray;
    }
}

The problem is that you initialize AmmunitionManager with the default constructor:

AmmunitionManager *var = new AmmunitionManager();

In you default constructor you do nothing so ammoArray may contain any value. It is better to initialize all the data to their default values:

AmmunitionManager::AmmunitionManager() : Ammo(), ammoArray(NULL/* or nullptr for C++11 */)
{
}

Now if you call for

var->FillAmmoArray(amVar.getSDamage(),amVar.getBDamage());

It will exit immediately since ammoArray is NULL. Or probably you want to initialize ammoArray anyway, so the default constructor should have its initialization as well:

AmmunitionManager::AmmunitionManager() : Ammo()
{
    ammoArray = new int* [10];
}

Also srand should be called only once, so better to place this code

srand(time(NULL));

in the main() or any other module which is guaranteed to be executed only once.

In the destructor, there is no need to zero *ammoArray=0 , it actually puts 0 at the first element of that array (and that's it), you anyway delete it. And imagine that ammoArray is NULL, accessing *ammoArray would cause another segmentation fault.

Also there is no need to check for ammoArray beibg NULL before deleting it. The standard allows to ' delete ' NULL pointers. delete will just return without doing nothing.

General note

It is better to use (safer and easier to maintain) std::vector instead of (dynamic) arrays and smart pointers instead of flat ones.

I'm not getting any errors (VS2013). But the values stored are the addresses of sDamage and bDamage.

Did you properly use AmmunitionManager(int sDamage,int bDamage) as a constructor for creating the AmmunitionManager object? From what I'm seeing, you're not.

Apart from that, may I ask why you're using exotic constructs such as **ammoArray instead of eg a simple vector<int> ? I'm guessing it's part of your assignment, but I'm asking just to make sure I'm not missing anything.

I called the object like this:

int _tmain(int argc, _TCHAR* argv[])
{
    AmmunitionManager* tc = new AmmunitionManager(5,10);
    tc->FillAmmoArray(10,10);

    return 0;
}

Why won't ammoArray store the values of bDamage and sDamage instead of the memory addresses of the array's elements?

Because you said it should store addresses.
Here is a pointer to a pointer:

int **ammoArray;

and here is an array of pointers:

ammoArray = new int* [10];

And How do I get it to store them?

By doing this instead:

int *ammoArray;

and this:

ammoArray = new int [10];

and adjusting FillAmmoArray accordingly.

The default constructor should look like this:

AmmunitionManager::AmmunitionManager()
  : ammoArray(nullptr)
{
}

The destructor should look like this:

AmmunitionManager::~AmmunitionManager()
{
    delete [] ammoArray;
}

And you should only call srand once.
It's usually done at the beginning of main .

It's a bit tricky answering without building and debugging, but the first thing that strikes me are: Why are you using pointers ( * ) to int throughout?

Why don't you just have the array as a pointer:

int *ammoArray;

and make the other int -instances (remove the pointers - * and the address-of 's ( & ))?

Regards

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM