I'm trying to implement a buffer block class in C++, along with another class that handles a least-recently-used (LRU) approach to buffers. My problem is that, when I try to access specific chars within buffer blocks, VS2017 throws an error at the line space[i] = bufferblocks[1][(i + pos) % 4];
:
Exception thrown: read access violation.
Bufferblock::operator[](...) returned 0xCDCDD173.
This is strange because, as far as I can tell, block
(in the Bufferblock
class) is initialized in the constructor to be a char array of 4096 chars, each of which is set to NULL (though I've also tried setting them to 'A' and the same thing happens). But then, when I cout
the sizeof(block)
, it prints "4".
Why is block
4 chars long and not 4096?
Here is what should be a minimal reproducible example of my code (mydatafile.txt
is just a really long text file - its contents shouldn't really matter):
#include <fstream>
#include <iostream>
using namespace std;
class Bufferblock {
public:
int blockID;
char* block;
int blockSize;
Bufferblock()
{
blockSize = 4096;
blockID = 1;
char *block = new char[4096];
for (int i = 0; i < 4096; i++)
{
block[i] = (char)NULL; // this works fine, looping through 4096 chars in block.
}
}
char& operator[](int index)
{
std::cout << sizeof(block) << std::endl; // prints 4, not 4096
return block[index % 4096]; // triggers exception that is thrown later
}
};
class LRUBufferPool {
private:
Bufferblock* bufferblocks;
public:
LRUBufferPool(string filename, int poolSize = 5, int blockSize = 4096)
{
bufferblocks = new Bufferblock[poolSize];
fstream input;
input.open(filename.c_str(), fstream::in, fstream::binary);
for (int i = 0; i < 5; i++)
{
input.read(bufferblocks[i].block, blockSize);
}
}
void getBytes(char* space, int sz, int pos) {
space = new char[sz];
for (int i = 0; i < sz; i++)
{
space[i] = bufferblocks[1][(i + pos) % 4096];
}
};
};
void initializeCharArray(int sz, char* ch) {
for (int i = 0; i < sz; i++) {
ch[i] = (char)NULL;
}
}
int main() {
LRUBufferPool* bp = new LRUBufferPool("mydatafile.txt", 5, 4096);
char* data = new char[10];
bp->getBytes(data, 10, 5030);
return 0;
}
(Please note that this project is very much in the beginning development phase. I'm not worried about memory leaks or really anything other than figuring this out just yet.)
Also, I tried modding by 4 instead of 4096 and it still doesn't work - it throws the same exception. This makes me thing that the issue might be with the initialization.
So, my questions are:
block
of length 4 and not 4096? Did I not initialize it correctly?I have to say it: Your code has many issues. You should not keep it for later to fix them. The longer you wait the more bugs similar to the current one will be encountered.
Just to name some:
new
. The first line in main
can simply be LRUBufferPool bp = LRUBufferPool("mydatafile.txt", 5, 4096);
. If you want to dynamically allocate it use a smart pointer.std::vector
or std::array
. Both manage their memory and both have a size()
method. If you do not want to use std::vector
or std::array
for learning purpose, write a class similar to std::vector
or std::array
. That class should do nothing but manage the array (read about the rule of 3/5! Your Bufferblock
will cause even more issues if copied).sizeof(some_pointer)
cannot tell you the size of the array pointed to by some_pointer
. In your case sizeof(block)
is the same as sizeof(char*)
which has nothing to do with the size of the array.Your problem reduced is:
struct Bufferblock {
int blockID;
char* block;
int blockSize;
Bufferblock() {
blockSize = 4096;
blockID = 1;
char *block = new char[4096];
for (int i = 0; i < 4096; i++)
{
block[i] = (char)NULL;
}
}
};
Your constructor has a local variable called block
that you initialize to point to an array of 4096 char
s. The member block
has the same name but is left uninitialized. Later trying to acces block[1]
dereferences an invalid pointer.
If you use std::vector
and the member initializer list the code is simpler and the problem gone:
struct Bufferblock {
int blockID;
std::vector<char> block;
Bufferblock() : blockID(1),block(4096) {}
};
If the block size is always 4096 you can use std::array
instead. In either case the blockSize
member is not needed and should be removed.