Heap corruption - Loading file (StaticMesh) - c++

I have the following class declared:
class StaticMesh
{
public:
unsigned int v_count;
float* vertices;
unsigned int n_count;
float* normals;
void Load_lin(const char* file);
void Draw(void);
void Release(void);
};
This class (as it's name indicates) represents a static mesh, which can load .lin files.
.lin files are generated by another application I made using C#. This application reads .obj files and generates the .lin file that has this structure:
v_count v
n_count n
a#a#a
b#b#b
a#a#a
b#b#b
Where v is the number of vertices, n the number of normals, and a/b represent coordinates.
Load_lin(const char*) is the function that loads these files, and here it is:
void StaticMesh::Load_lin(const char* file)
{
std::ifstream in (file);
if (!in)
{
std::cout << "Error: Failed to load staticmesh from '" << file << "'." << std::endl;
return;
}
char buffer[256];
in.getline(buffer, 256);
sscanf_s(buffer, "v_count %i", &v_count);
in.getline(buffer, 256);
sscanf_s(buffer, "n_count %i", &n_count);
vertices = new float[v_count];
normals = new float[n_count];
unsigned int a = 0;
unsigned int p = 0;
float x, y, z;
do
{
in.getline(buffer, 256);
if (buffer[0] == '\n' || buffer[0] == '\r') break;
sscanf_s(buffer, "%f#%f#%f", &x, &y, &z);
vertices[a++] = x;
vertices[a++] = z;
vertices[a++] = y;
in.getline(buffer, 256);
sscanf_s(buffer, "%f#%f#%f", &x, &y, &z);
normals[p++] = x;
normals[p++] = z;
normals[p++] = y;
} while (!in.eof());
in.close();
}
I've narrowed down the cause of the error to this function, however, the error only shows when the application is closed, and sometimes it doesn't happen.
So the line where the error occurs is actually the end of WinMain:
return msn.message;
I've went further and used std::cout to print the variables 'a' and 'p', this causes an heap corruption error, but this time in malloc.c line 55:
__forceinline void * __cdecl _heap_alloc (size_t size)
{
if (_crtheap == 0) {
_FF_MSGBANNER(); /* write run-time error banner */
_NMSG_WRITE(_RT_CRT_NOTINIT); /* write message */
__crtExitProcess(255); /* normally _exit(255) */
}
return HeapAlloc(_crtheap, 0, size ? size : 1);
} // LINE 55
I've searched for this last error with no avail.
Thank you for your time. :)

I think v_count and n_count mentions the number of vertices. According to the code, each vertex will have 3 components (x/y/z) and each component is stored in a float variable. This means that you need to allocate 3 times v_count and 3 times n_count number of floats for vertices and normals respectively.
i.e modify your allocation as
vertices = new float[v_count * 3];
normals = new float[n_count * 3];

v_count v means the number you read is (unsigned int)'v'. And then you have vertices = new float[v_count];. It's likely you don't allocate enough storage and write outside the bounds of the arrays vertices and normals resulting in undefined behavior.

Just try re-write your code to use std::vector instead of raw pointers. This will really help you avoid issues with heap corruption.

Related

Best practices for large array of integers in C++

I am loading a 123 MB file of unsigned integers that needs to be in memory (for fast look ups for a monte carlo simulation) in C++. Right now I have a global array but i've heard global arrays are frowned upon. What are the best practices for this?
For context, I'm doing Monte Carlo simulations on a poker game and need an array of about 30 million integers to quickly compute the winner of a poker hand. To determine the winner, you first compute the 'handranks' by doing 7 queries of the array. Then to determine the winner, you compare the 'handranks'.
int HR[32487834];
int get_handrank(const std::array<int,7> cards)
{
int p = 53;
for (const auto& c: cards)
p = HR[p + c];
return p;
}
int main()
{
// load the data
memset(HR, 0, sizeof(HR));
FILE * fin = fopen("handranks.dat", "rb");
if (!fin)
std::cout << "error when loading handranks.dat" << std::endl;
size_t bytesread = fread(HR, sizeof(HR), 1, fin);
fclose(fin);
std::cout << "complete.\n\n";
// monte carlo simulations using get_handrank() function
.
.
.
}
Use local variables, and pass them to functions as appropriate. This makes the program easier to reason about.
Use vector instead of an array for this large amount of data, otherwise you might cause stack overflow.
Modify your functions to work with std::span instead of a particular container, as this creates more decoupling.
Create a symbolic constant with a meaningful name for 32487834 instead of using it as a magic constant.
Using local variables and passing them around is always a better practice than having globals IMO. It makes your algorithms more flexible. What if you need to use a different array for some reason later on? You would need to modify all the functions using the global variable. Passing around the array is a bit inconvenient and verbose I agree, but still better than the globals. We will address this problem later on.
So your first option is something like:
// Don't forget to receive the params as references to avoid copying
int get_handrank(const std::array<int,7>& cards, const std::array<int, 32487834>& HR)
{
int p = 53;
for (const auto& c: cards)
p = HR[p + c];
return p;
}
int main()
{
std::array<int, 32487834> HR{}; //zero-inits the array
// or std::vector<int> HR{}; HR.resize(32487834) to avoid stack overflow as #MarkRansom pointed out
FILE * fin = fopen("handranks.dat", "rb");
if (!fin)
std::cout << "error when loading handranks.dat" << std::endl;
size_t bytesread = fread(HR.data(), HR.size() * sizeof(int), 1, fin);
fclose(fin);
}
Second approach, even better: Use classes. You can have a simulation class, and the class can have the HR array as a const private member read and initialized in the constructor. Then get_handrank can be a member function and can access the member HR array:
class Simulation {
public:
int get_handrank(const std::array<int,7>& cards)
{
int p = 53;
for (const auto& c: cards)
p = HR[p + c];
return p;
}
Simulation()
: HR{}
// or HR{readHRFromFileFunction()}
{
FILE * fin = fopen("handranks.dat", "rb");
if (!fin)
std::cout << "error when loading handranks.dat" << std::endl;
size_t bytesread = fread(HR.data(), HR.size() * sizeof(int), 1, fin);
fclose(fin);
}
private:
std::array<int, 32487834> HR;
//or const std::array<int, 32487834> HR; if you use a function to init it
}

Wrong pixel values when using padded local buffer OpenCL

I'm facing an unexpected result when I use a local buffer to copy data in an OpenCL kernel. The code presented here is quite simple (and useless since I don't need to use a local buffer for such an operation), but this is a first step for convolution-like processes.
Here is my code :
std::string implementCopyFromLocalKernel()
{
return BOOST_COMPUTE_STRINGIZE_SOURCE(
__kernel void copyFromLocal_knl(__global const float* in,
const ulong sizeX, const ulong sizeY,
const int filterRadiusX, const int filterRadiusY,
__local float* localImage,
const ulong localSizeX, const ulong localSizeY,
__global float* out)
{
// Store each work-item’s unique row and column
const int x = get_global_id(0);
const int y = get_global_id(1);
// Group size
int groupSizeX = get_local_size(0);
int groupSizeY = get_local_size(1);
// Determine the size of the work group output region
int groupIdX = get_group_id(0);
int groupIdY = get_group_id(1);
// Determine the local ID of each work item
int localX = get_local_id(0);
int localY = get_local_id(1);
// Padding
int paddingX = filterRadiusX;
int paddingY = filterRadiusY;
// Cache the data to local memory
// Copy the data for the current coordinates
localImage[localX + localY*localSizeX] = in[x + y * sizeX];
barrier(CLK_LOCAL_MEM_FENCE);
out[x + y * sizeX] = localImage[localX + localY*localSizeX];
return;
}
);
}
void copyLocalBuffer(const boost::compute::context& context, boost::compute::command_queue& queue, const boost::compute::buffer& bufInn boost::compute::buffer& bufOut, const size_t sizeX, const size_t sizeY)
{
const size_t nbPx = sizeX * sizeY;
const size_t maxSize = (sizeX > sizeY ? sizeX : sizeY);
// Prepare to launch the kernel
std::string kernel_src = implementCopyFromLocalKernel();
boost::compute::program program;
try {
program = boost::compute::program::create_with_source(kernel_src, pGpuDescription->getContext(deviceIdx));
program.build();
}
catch (const boost::compute::opencl_error& e) {
std::cout << "Error bulding program from source : " << std::endl << e.what() << std::endl
<< program.build_log() << std::endl;
return;
}
boost::compute::kernel kernel;
try {
kernel = program.create_kernel("copyFromLocal_knl");
}
catch (const boost::compute::opencl_error& e) {
std::cout << "Error creating kernel : " << std::endl << e.what() << std::endl;
return;
}
try {
int localSizeX = 16;
int localSizeY = 16;
int paddingPixelsX = 2;// 0; // <- Changing to 0 works
int paddingPixelsY = paddingPixelsX;
int localWidth = localSizeX + 2 * paddingPixelsX;
int localHeight = localSizeY + 2 * paddingPixelsY;
boost::compute::buffer localImage(context, localWidth*localHeight * sizeof(float));
kernel.set_arg(0, bufIn);
kernel.set_arg(1, sizeX);
kernel.set_arg(2, sizeY);
kernel.set_arg(3, paddingPixelsX);
kernel.set_arg(4, paddingPixelsY);
kernel.set_arg(5, localImage);
kernel.set_arg(6, localWidth);
kernel.set_arg(7, localHeight);
kernel.set_arg(8, bufOut);
}
catch (const boost::compute::opencl_error& e) {
std::cout << "Error setting kernel arguments: " << std::endl << e.what() << std::endl;
return;
}
try {
size_t origin[2] = { 0, 0 };
size_t region[2] = { 256, 256 };// { sizeX, sizeY };
size_t localSize[2] = { 16, 16 };
queue.enqueue_nd_range_kernel(kernel, 2, origin, region, localSize);
}
catch (const boost::compute::opencl_error& e) {
std::cout << "Error executing kernel : " << std::endl << e.what() << std::endl;
return;
}
}
I reduced the code to simply copy the pixels corresponding to each work item in the associated local coordinate of the local image. Hence, the local image buffer must have unused data for 2*paddingPixelsX on each line and 2*paddingPixelsY unused lines.
It works if I don't add padding data (paddingPixelsX and paddingPixelsY = 0), but it seems that some work items don't read the data from the input buffer or write the data into the ouput buffer (or the local buffer?) in the correct place. Moreover, when I run my program several times, I never get the same result.
This is an example of result I get (right) for the mandrill image as input (left) :
I ensure that the threads are synchronized with barrier(CLK_LOCAL_MEM_FENCE); and each work item read and write a specific data and if my code is buggy, I don't understand why no padding don't gives errors.
Does someone has an idea?
Thanks,
As already confirmed, the problem was that dynamically allocated local buffer passed to the kernel was only created for one work group.
One of the solutions is to create local buffer statically inside the kernel, for example:
__local float localImage[16*16];
If the size of the buffer cannot be hard coded then it could be set via preprocessor:
__local float localImage[SIZE_X*SIZE_Y];
which then these parameters are passed during kernel build.
From what I remember using kernel parameters to define size of static local buffer may not work for every GPU (compilation will fail).
I'm not familiar with boost compute but I presume something similar should be possible to achieve by passing parameters to implementCopyFromLocalKernel() which then they would be converted into values during stringizing.
Thanks to #doqtor, I understood that the issue came from the buffer passed as kernel parameter. Because of that, all work group used the same buffer.
Since I don't know the padding size I will need for convolution operations, I need this buffer as parameter. I modified the kernel parametrization so that a different buffer is used by each work group :
kernel.set_arg(5, localWidth*localHeight*sizeof(float), NULL);
I missed the important part when I read the documentation of clSetKernelArg:
If the argument is declared with the __local qualifier, the arg_value entry must be NULL.

C++ Deep Copy Object

I am trying to deep copy objects back and forth. When I run the gdb, I get the following error after one iteration of the loop.
Program received signal SIGSEGV, Segmentation fault.
0x0804ab96 in DGCPM::DGCPM (this=0x844b760, cur=0x1) at DGCPM.C:27
27 memcpy(vRCells, cur->vRCells,sizeof(float)*nThetaCells);
I suspect the problem has to do with creating the "new class," but I'm not sure. Any suggestions?
(Note: The "_initialize" code calls a FORTRAN subroutine that sets the values in the program.)
Here is the run.C main file:
#include "../include/DGCPM.h"
#define particle_num 5
class DGCPM **mallocModels(int n);
int main(int argc, char *argv[]){
class DGCPM **m;
class DGCPM **cur;
m=mallocModels(particle_num);//update
for(int t = 0; t < 48; t++){
//Update m, and then...
cur = m;
m = (DGCPM**)malloc(sizeof(class DGCPM *)*particle_num);
for(int i=0;i<particle_num;i++){
randomidx = ((double)rand() / ((double)RAND_MAX + 1));
currentidx = find(cumPw,randomidx,particle_num);
m[i] = new class DGCPM(cur[currentidx]);
}
for(int i=0;i<particle_num;i++){
delete cur[i];
}
free(cur);
}
return 0;
}
/*============================================================================
mallocModels - allocate the ensemble of models
============================================================================*/
class DGCPM **mallocModels(int n){
class DGCPM **m;
m=(class DGCPM **)amjSafeMalloc(sizeof(class DGCPM *)*n,
(char *)"mallocModels:m");
for(int i=0;i<n;i++)
m[i]=new class DGCPM();
return m;
}
/*============================================================================
Find - Return a particle index that has a high probability of having a high weight.
============================================================================*/
int find(float *cumPw, double randomidx, int nM){
/*Wrong implementation*/
int index = 0;
flag = 0;
while(flag == 0){
if(cumPw[i] >= randomidx){
flag = 1;
i++;
}
else{
index ++;
}
}
return index; //Sometimes, index was going to number of models, or number of models + 1, which are out of bounds.
/*Correct implementation*/
int index = 0;
for(int i = 0; i < nM-1; i++){
if(cumPw[i] >= randomidx){
index = i;
break;
}
}
if(index >= nM){
index = nM-1;
printf("Error: random index exceeds bounds");
}
return index;
}
Here is the DGCPM.h header file:
class DGCPM{
public:
DGCPM(); /* Initialized with defaults setup */
DGCPM(class DGCPM *cur); //Copy constructor
DGCPM(int nThetaCells, int nPhiCells, float thetaMin, float thetaMax);
~DGCPM(); /* Free memory */
private:
int internal; /* 1=memory allocated internally and should be deallocated when ~DGCPM is called, 2=memory is internal except for mGridN which is external */
int nThetaCells,nRCells,nPhiCells;
float thetaMin,thetaMax;
float rMin,rMax;
float delR,delPhi;
float deltMax;
float *vRCells; /* [nThetaCells] */
float *vThetaCells; /* [nThetaCells] */
float *vPhiCells; /* [nPhiCells] */
float **mGridB; /* [nPhiCells][nThetaCells] */
float **mGridBi; /* [nPhiCells][nThetaCells] */
float **mGridPot; /* [nPhiCells][nThetaCells] */
float **mGridEr; /* [nPhiCells][nThetaCells] */
float **mGridEp; /* [nPhiCells][nThetaCells] */
float **mGridVr; /* [nPhiCells][nThetaCells] */
float **mGridVp; /* [nPhiCells][nThetaCells] */
float **mGridN; /* [nPhiCells][nThetaCells] */
float **mGridHalf; /* [nPhiCells][nThetaCells] Particles / weber (workspace for upwind and superbee) */
float **mGridDen; /* [nPhiCells][nThetaCells] */
float **mGridVol; /* [nPhiCells][nThetaCells] */
float **mGridX; /* [nPhiCells][nThetaCells] */
float **mGridY; /* [nPhiCells][nThetaCells] */
float **mGridOc; /* [nPhiCells][nThetaCells] */
float **std; /* [nPhiCells][nThetaCells] */
float parI[2];
float delTMax;
float Re;
void initialize(int nThetaCells, int nPhiCells, float thetaMin,
float thetaMax);
};
And finally the DGCPM.C object wrapper:
/******************************************************************************
* DGCPM.C - This implements the DGCPM plasmasphere model class *
******************************************************************************/
#define TWO_PI 6.2831853071795864769252866
#include "../include/DGCPM.h"
# include <cstdlib>
# include <cmath>
/*============================================================================
DGCPM::DGCPM()
Initialize with default setup
============================================================================*/
DGCPM::DGCPM(){
internal=1;
initialize(200,200,14.963217,60.0);/*(180,200,14.963217,60.0);*/
}
//Copy Constructor
DGCPM::DGCPM(class DGCPM *cur){
internal=1;
initialize(200,200,14.963217,60.0);/*(180,200,14.963217,60.0);*/
memcpy(vRCells, cur->vRCells,sizeof(float)*nThetaCells);
memcpy(vPhiCells, cur->vPhiCells,sizeof(float)*nPhiCells);
memcpy(vThetaCells, cur->vThetaCells,sizeof(float)*nThetaCells);
memcpy(mGridB[0], cur->mGridB[0],sizeof(float)*nThetaCells*nPhiCells);
memcpy(mGridBi[0], cur->mGridBi[0],sizeof(float)*nThetaCells*nPhiCells);
memcpy(mGridPot[0], cur->mGridPot[0],sizeof(float)*nThetaCells*nPhiCells);
memcpy(mGridEr[0], cur->mGridEr[0],sizeof(float)*nThetaCells*nPhiCells);
memcpy(mGridEp[0], cur->mGridEp[0],sizeof(float)*nThetaCells*nPhiCells);
memcpy(mGridVr[0], cur->mGridVr[0],sizeof(float)*nThetaCells*nPhiCells);
memcpy(mGridVp[0], cur->mGridVp[0],sizeof(float)*nThetaCells*nPhiCells);
memcpy(mGridN[0], cur->mGridN[0],sizeof(float)*nThetaCells*nPhiCells);
memcpy(mGridHalf[0], cur->mGridHalf[0],sizeof(float)*nThetaCells*nPhiCells);
memcpy(mGridDen[0], cur->mGridDen[0],sizeof(float)*nThetaCells*nPhiCells);
memcpy(mGridVol[0], cur->mGridVol[0],sizeof(float)*nThetaCells*nPhiCells);
memcpy(mGridOc[0], cur->mGridOc[0],sizeof(float)*nThetaCells*nPhiCells);
memcpy(mGridX[0], cur->mGridX[0],sizeof(float)*nThetaCells*nPhiCells);
memcpy(mGridY[0], cur->mGridY[0],sizeof(float)*nThetaCells*nPhiCells);
memcpy(std[0], cur->std[0],sizeof(float)*nThetaCells*nPhiCells);
}
/*============================================================================
DGCPM::~DGCPM()
Free allocated memory
============================================================================*/
DGCPM::~DGCPM(){
if(internal>=1){
amjFree1dFloat(vRCells);
amjFree1dFloat(vThetaCells);
amjFree1dFloat(vPhiCells);
amjFree2dFloat(mGridB);
amjFree2dFloat(mGridBi);
amjFree2dFloat(mGridEr);
amjFree2dFloat(mGridEp);
amjFree2dFloat(mGridVr);
amjFree2dFloat(mGridVp);
if(internal==1) amjFree2dFloat(mGridN);
amjFree2dFloat(mGridHalf);
amjFree2dFloat(mGridDen);
amjFree2dFloat(mGridVol);
amjFree2dFloat(mGridX);
amjFree2dFloat(mGridY);
amjFree2dFloat(mGridOc);
amjFree2dFloat(std);
}
}
/******************************************************************************
******************************************************************************
** Private functions **
******************************************************************************
******************************************************************************/
/*============================================================================
DGCPM::initialize(int nThetaCells, int nPhiCells, float thetaMin,
float thetaMax);
This is the initialization function used when all memory should be
allocated internally.
============================================================================*/
void DGCPM::initialize(int nThetaCells, int nPhiCells, float thetaMin,
float thetaMax){
initialize(nThetaCells,nPhiCells,thetaMin,thetaMax,
amjMalloc1dFloat(nThetaCells,(char *)"DGCPM::DGCPM:vRCells"),
amjMalloc1dFloat(nThetaCells,(char *)"DGCPM::DGCPM:vThetaCells"),
amjMalloc1dFloat(nPhiCells,(char *)"DGCPM::DGCPM:vPhiCells"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridB"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridBi"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridPot"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridEr"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridEp"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridVr"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridVp"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridN"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridHalf"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridDen"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridVol"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridX"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridY"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridOc"),
//Added by J.Wise
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:std"));
}
/*============================================================================
DGCPM::initialize(int nThetaCells, int nPhiCells, float thetaMin,
float thetaMax);
This is the initialization function used when mGridN is passed from
the outside but all other memory is allocated internally.
============================================================================*/
void DGCPM::initialize(int nThetaCells, int nPhiCells, float thetaMin,
float thetaMax, float **mGridN){
initialize(nThetaCells,nPhiCells,thetaMin,thetaMax,
amjMalloc1dFloat(nThetaCells,(char *)"DGCPM::DGCPM:vRCells"),
amjMalloc1dFloat(nThetaCells,(char *)"DGCPM::DGCPM:vThetaCells"),
amjMalloc1dFloat(nPhiCells,(char *)"DGCPM::DGCPM:vPhiCells"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridB"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridBi"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridPot"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridEr"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridEp"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridVr"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridVp"),
mGridN,
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridHalf"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridDen"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridVol"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridX"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridY"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridOc"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:std"));
}
/*
initialize() - this initialization function uses pre-allocated
memory areas passed in from the outside. This function is used both
when DGCPM allocates memory itself and when it receives
pre-allocated memory from the outside in order to eliminate
duplication of code with the associated risk of errors.
============================================================================*/
void DGCPM::initialize(int nThetaCells, int nPhiCells, float thetaMin,
float thetaMax, float *vRCells, float *vThetaCells,
float *vPhiCells, float **mGridB, float **mGridBi,
float **mGridPot, float **mGridEr, float **mGridEp,
float **mGridVr, float **mGridVp, float **mGridN,
float **mGridHalf, float **mGridDen, float **mGridVol,
float **mGridX, float **mGridY, float **mGridOc, float **std){
DGCPM::nThetaCells=nThetaCells;
DGCPM::nPhiCells=nPhiCells;
DGCPM::thetaMin=thetaMin;
DGCPM::thetaMax=thetaMax;
DGCPM::vRCells=vRCells;
DGCPM::vThetaCells=vThetaCells;
DGCPM::vPhiCells=vPhiCells;
DGCPM::mGridB=mGridB;
DGCPM::mGridBi=mGridBi;
DGCPM::mGridPot=mGridPot;
DGCPM::mGridEr=mGridEr;
DGCPM::mGridEp=mGridEp;
DGCPM::mGridVr=mGridVr;
DGCPM::mGridVp=mGridVp;
DGCPM::mGridN=mGridN;
DGCPM::mGridHalf=mGridHalf;
DGCPM::mGridDen=mGridDen;
DGCPM::mGridVol=mGridVol;
DGCPM::mGridX=mGridX;
DGCPM::mGridY=mGridY;
DGCPM::mGridOc=mGridOc;
DGCPM::std=std;
Re=6.378e6;
initialize_(&nThetaCells,&nRCells,&nPhiCells,&thetaMin,&thetaMax,&rMin,&rMax,
&delR,&delPhi,vRCells,vThetaCells,vPhiCells,mGridB[0],mGridBi[0],
mGridN[0],mGridDen[0],mGridVol[0],mGridX[0],mGridY[0],mGridOc[0],std[0]);
}
Here's a sample custom memory function, which takes care of initialization and allocation:
void *amjSafeMalloc(int n, char *message){
void *d;
d=malloc(n);
if(d==NULL){
fprintf(stderr,"amjSafeMalloc error: Could not allocate %d bytes "
"for %s. Exiting.\n",n,message);
exit(1);
}
return d;
}
float *amjMalloc1dFloat(int a, char *message){
float *d;
sprintf(msg,"%s:amjMalloc1DFloat:d",message);
d=(float *)amjSafeMalloc(sizeof(float)*a,msg);
return d;
}
float **amjMalloc2dFloat(int a, int b, char *message){
float **d;
int i;
sprintf(msg,"%s:amjMalloc2DFloat:d",message);
d=(float **)amjSafeMalloc(sizeof(float *)*a,msg);
sprintf(msg,"%s:amjMalloc2DFloat:d[0]",message);
d[0]=(float *)amjSafeMalloc(sizeof(float)*a*b,msg);
for(i=1;i<a;i++) d[i]=d[i-1]+b;
return d;
}
class DGCPM
{
public:
DGCPM(int nThetaCells, int nPhiCells)
: nThetaCells(nThetaCells)
, nPhiCells(nPhiCells)
, mGridB(nThetaCells, vector<float>(nPhiCells)) // first Y then X
{
}
private:
int nThetaCells, nPhiCells;
vector<vector<float>> mGridB;
};
Deep copies for free. Deletes memory for free.
By free I mean you don't have to write the code..
From your comment /* [nPhiCells][nThetaCells] */ in your class definition, I take it that you intent the float** to be 2D arrays. However, if you can use them like 2D arrays, they are actually arrays of pointers to arrays. That is a huge difference: it means, you have to copy nPhiCells individual arrays of nThetaCells elements and you have to setup the pointer array itself. Now, when you do
memcpy(mGridHalf[0], cur->mGridHalf[0],sizeof(float)*nThetaCells*nPhiCells);
in your copy constructor, you assume that there is no pointer array, and that all line arrays are sequential in memory. Either this copy exceeds the bounds of the pointer array (segfaulting), or accessing you array via mGridHalf[i][j] simply does the wrong thing, reinterpreting float data as pointers (and segfaulting).
Unfortunately, C++ is a horrible language for interacting with fortran multidimensional arrays because it has no notion of variable sized arrays. So the following is C code, not C++ code. In C, you can tackle the issue like this:
float (*mGridHalf)[nThetaCells] = malloc(nPhiCells*sizeof(*mGridHalf));
will correctly allocate and type a 2D array (i. e. an array of arrays) that can be accessed with
mGridHalf[phi][theta] = 7.3;
Since all elements are consecutive in memory, the entire thing can correctly be copied with
memcpy(mGridHalf, cur->mGridHalf, nPhiCells*sizeof(*mGridHalf));
and freed with
free(mGridHalf);
Technically, mGridHalf is now a pointer to an array, the pointer arithmetic that is invoked by the array access effectively does the same computation as if you had written:
float* foo = malloc(nPhiCells*nThetaCells*sizeof(*foo));
foo[phi*nThetaCells + theta] = 7.3;
However, using the correct pointer type float (*)[nThetaCells] allows you to avoid doing the index computation yourself.
The issue is more than likely you're assuming that float** has data that is one contiguous chunk of memory. If so, here is one way of accomplishing this. First, I show the wrong way (but used often):
float** createFloat2D(int nRows, int nCols)
{
float** p1 = new float*[nRows];
for (int i = 0; i < nCols; ++i )
p1[i] = new float[nCols];
return p1;
}
void destroyFloat2D(float**f, int nRows, int nCols)
{
for (int i = 0; i < nCols; ++i )
delete [] f[i];
delete [] f;
}
Looks simple, and works for most purposes, but will fail if the assumption is made that the data is in a contiguous chunk of memory.
The other way to create a 2D array is to make the data contiguous.
float** createFloat2D(int nRows, int nCols)
{
float** p1 = new float*[nRows]; // allocate row pointers
float* p2 = new float[nRows * nCols]; // allocate data in one chunk
for (int i = 0; i < nCols; ++i, p2 += nCols )
p1[i] = p2; // point the row pointers into the pool of memory
return p1;
}
void destroyFloat2D(float**f)
{
delete [] f[0];
delete [] f;
}
Note above that the data is created in one contiguous "pool". Now, using yourArray[0] actually points to the beginning of this memory. Also note that destruction is done without having to know the number of rows or columns, since f[0] points to the pool of memory.
So now, code like this should work
float** mGridB = createFloat2D(nThetaCells, nPhiCells);
//...
memcpy(mGridB[0], cur->mGridB[0], sizeof(float)*nThetaCells*nPhiCells);
The code above now works correctly, if we use the second method of creating the 2d array.
I would still stick with the vector for 1-d float arrays, as you have the pointer to the data (see my earlier comment). For the code above, I would wrap it in a class that handles creation and destruction easily.
The last thing is the copy constructor. A copy constructor in C++ has the following possible signatures:
DGCPM(const DGCPM&);
DGCPM(DGCPM&);
DGCPM(volatile DBCPM&);
I may have missed one, but the signature should be one of those above, more than likely, the first one (you can also have additional arguments after the reference argument, but they all must have default values).
Note that a DBCPM* is not a valid argument for a copy constructor as your code stated -- remember that a copy constructor is not only for use, but also the compiler will use it to make copies. So to signal the compiler that "yes, this function is used to make copies", your function must match one of the signatures above.
In addition, you need an assignment operator, in other words, the class needs to implement the "rule of 3".
This going to sound so stupid (elementary programming error): my index "i" was going beyond (number of models - 1), so I was getting a segmentation fault from accessing memory that didn't exist.

Segfault with std::list usage

I'm Java user coming over to C++, and I am having a hard time understanding what is going wrong with this statement. My program has been segfaulting anywhere I put the push_back command. So I'm wondering what exactly is going on.
class Process {
public:
int nice;
int arrivalTime;
int cpuBursts;
list<int> burstList;
Process() {
burstList.push_back(10); // Segfaults here...
}
};
Here is the full code:
#include<iostream>
#include<stdlib.h>
#include<fstream>
#include<list>
#include<string.h>
using namespace std;
int calcTimeslice(int priority);
int calcOriginalPrio(int nice);
int readFile(int ,char **);
int calcPrioBonus(int,int);
void tokenizeAndAdd(char *);
class Bursts {
public:
int isCPUBurst;
int time;
Bursts() {}
// Constructor to make it easier to add to list
Bursts(int tempIsCPU, int tempTime) {
isCPUBurst = tempIsCPU;
time = tempTime;
}
};
class Process {
public:
int nice;
int arrivalTime;
int cpuBursts;
list<int> burstList;
Process() {
burstList.push_back(10);
}
};
int main(int arg, char **argv) {
// This is if the file was not correctly read into the program
// or it doesnt exist ...
if(readFile(arg,argv)==-1) {
cout << "File could not be read. \n";
return -1;
}
//cout << "Original Calc Whatever: " << calcOriginal(19) << '\n';
return 0;
}
/*
* Calculates the timeslice based on the priority
*/
int calcTimeslice(int priority) {
double finalCalc;
// This is the given function in the prompt
finalCalc = ( (1 - (priority / 140)) * 290 + (.5) ) + 10;
// Cast to int, this will be a truncate
return ((int)finalCalc);
}
int readFile(int arg, char **argv) {
char *temp,*pointer;
int endOfFile = 1;
// While its not the end of the file
while(endOfFile) {
// Read in the input from stdin
fgets(temp,256,stdin);
// Check to see if this line had a * in it
if(*temp =='*')
endOfFile = 0;
else
tokenizeAndAdd(temp);
}
return 0;
}
void tokenizeAndAdd(char *string) {
char *token = strtok(string," \n");
int i = 0;
Process p;
while(token != NULL) {
cout << token << endl;
if(i>2) { // If it is odd (CPU burst)
if(i%2 == 1) {
int tempInt = atoi(token);
//p.burstList.push_back(tempInt);
}
else { // If it is even (IO burst)
int tempInt = atoi(token);
//p.burstLis.push_back(tempInt);
}
}
else if(i==0)
p.nice = atoi(token);
else if(i==1)
p.arrivalTime = atoi(token);
else if(i==2)
p.cpuBursts = atoi(token);
token = strtok(NULL," \n");
i++;
}
//cout << p.nice << " " << p.arrivalTime << " " << p.cpuBursts << "\n";
//i = 0;
//cout << p.burstList.size() << "\n";
// cout <<
//}
return;
}
/*
* Calculates and returns the original priority based on the nice number
* provided in the file.
*/
int calcOriginalPrio(int nice) {
double finalCalc;
// This is the given function from the prompt
finalCalc = (( nice + 20 ) / 39 ) * 30 + 105.5;
// Cast to int, this is a truncate in C++
return ((int)finalCalc);
}
/*
* Calculates the bonus time given to a process
*/
int calcPrioBonus(int totalCPU, int totalIO) {
double finalCalc;
// How to calculate bonus off of the prompt
if(totalCPU < totalIO)
finalCalc = ( (1 - (totalCPU / (double)totalIO)) * (-5)) - .5;
else
finalCalc = ( (1 - (totalIO / (double)totalCPU)) * 5) + .5;
// Cast to int
return ((int)finalCalc);
}
You are using temp uninitialized in the following code:
char *temp;
...
while(endOfFile) {
fgets(temp,256,stdin);
...
This can have any side effect, since it most likely destroys your stack or parts of the heap memory. It could fail immediately (when calling the fgets() function), it could fail later (as in your sample) or it could even run fine - maybe until you upgrade your OS, your compiler or anything else, or until you want to run the same executable on another machine. This is called undefined behaviour.
You need to allocate space for the temp variable, not a pointer only. Use something like
char temp[256];
...
while(endOfFile) {
fgets(temp,256,stdin);
...
For more information, see the fgets() documentation. The first parameter is a pointer to a char array - that is where fgets() will store the bytes which have been read. In your code, you pass an uninitialized pointer which means that fgets() will store the bytes to an undefined memory location - this is catched by the OS which terminates your application with a segmentation fault.
BTW: You should consider enabling pedantic warnings when compiling - I compiled with
g++ -Wall -pedantic -o list list.cpp
which gave me the following warning:
list.cpp: In function 'int readFile(int, char**)':
list.cpp:76:26: warning: 'temp' may be used uninitialized in this function [-Wuninitialized]
This is probably not the actual code with the error you report. But here is one of the problems with give you UB.
char *temp,*pointer; // uninicialized pointer char temp[1000]; could work?
int endOfFile = 1;
// While its not the end of the file
while(endOfFile) {
// Read in the input from stdin
fgets(temp,256,stdin);
The last function call will read a maximum of 256 bytes from stdin and will write it in the memory pointed by pointer tmp. So, you need to first "prepare" that memory. But with char *tmp; you only define a pointer, with no defined value, that is, with point to some possible unexisting or illegal/inaccessible for you memory. In contrary, char tmp[1000]; will define in the "stack memory" a block of 1000 bytes, with you can point to using simple the variable tmp. Hope this is clear for you.
EDIT:
I don't know why that would change the behavior of the list,
You are right. That is Undefined Behavior (UB). When you write in some unknown memory (pointed by an uninitialized pointer) you may overwrite data or even code that will broke somewhere the correct function of your program in an unpredicted way.
You will need to learn more about pointers but better you use std::string, and look how parse your file using string and stringstream. That will manage for you the memmory,

Assigning and retrieving bit-wise memory value for Genetic Algo

I came across this code for developing a class for GA/GP but failed to understand it and hence unable debug the program.
typedef struct {
void *dataPointer;
int length;
} binary_data;
typedef struct {
organism *organisms; //This must be malloc'ed
int organismsCount;
int (*fitnessTest)(organism org);
int orgDnaLength;
unsigned int desiredFitness;
void (*progress)(unsigned int fitness);
} evolutionary_algorithm;
The above is straight forward. Then we try to initiate organism before testing their fitnness etc...
int main(int argc, char *argv[])
{
srand(time(NULL));
int i;
evolutionary_algorithm ea;
ea.progress = progressDisplayer;
ea.organismsCount = 50;
ea.orgDnaLength = sizeof(unsigned int);
organism *orgs =(organism *) malloc(sizeof(organism) * ea.organismsCount);
for (i = 0; i < 50; i++)
{
organism newOrg;
binary_data newOrgDna;
newOrgDna.dataPointer = malloc(sizeof(unsigned int));
memset(newOrgDna.dataPointer, i, 1);
newOrgDna.length = sizeof(unsigned int);
newOrg.dna = newOrgDna;
orgs[i] = newOrg;
}
As far as i understand is the memset() tries to write a binary value into that memory location void pointer (newOrgDna.dataPointer) and so on. But i cant figure how to reassemble all those binary values to get the integer value assigned to variable "dna" of newOrg so that i check the integer value assign to the an individual organism and eventually the entire population residing in the entire memory location which has been assigned to "orgs".
As you guess from above, i not very familiar memory management at this deep level of details so your help is very much appreciated.
Thank you so much
This code looks a bit strange. This line:
newOrgDna.dataPointer = malloc(sizeof(unsigned int));
will allocate probably 4 bytes (or 8 on 64 bit machines). Strange part is that memset in line just below will set only first byte.
To get actual value you might do:
char val = *((char*) newOrgDna.dataPointer);
But, as I said, this code looks a bit off. I would rewrite it as:
for (i = 0; i < 50; i++)
{
organism newOrg;
binary_data newOrgDna;
unsigned int * data = (unsigned int*) malloc(sizeof(unsigned int));
*data = i;
newOrgDna.length = sizeof(*data);
newOrgDna.data = (void*) data; // I think that cast can be dropped
newOrg.dna = newOrgDna;
orgs[i] = newOrg;
}
Then everywhere you want to get data from organism * you can do:
void f( organism * o )
{
assert( sizeof(unsigned int) == o->dna.length );
unsigned int data = *((unsigned int*) o->dna.data);
}
Also this is rather a C question not C++.