Correct way to write a function that return an n-dimensional array of TH1 objects

Hello,

I’m trying to write a function that return an array of TH1D histograms.
I follow a post on stackoverflow, which recommended using malloc to create the array of histograms. I follow the instruction and my code works.
The code I wrote is as follow:

TH1D** *array_of_hists( )
{
	TH1D** *hist = ( TH1D*** )malloc( sizeof(TH1D**)*2 );
	for ( int i=0; i<2; i++ )
	{
		hist[i] = ( TH1D** )malloc( sizeof(TH1D*)*5 );
		for ( int j=0; j<2; j++ )
		{
			std::string hist_name = Form ( "hist_%d_%d", i, j );
			hist[i][j] = new TH1D( hist_name.data( ), "", 20, -5, 5 );
			hist[i][j] -> FillRandom( "gaus", 10000 );
		}
	}
	return hist;
}

int test_TH1_array( )
{
	system( "mkdir -p Plot" );
	TH1D** *hist = array_of_hists( );
	for ( int i=0; i<2; i++ )
		for ( int j=0; j<2; j++ )
		{
			TCanvas *canvas = new TCanvas( "canvas", "", 600, 400 );
			canvas -> cd( );
			hist[i][j] -> SetLineColor( kOrange+7 );
			hist[i][j] -> SetLineWidth( 2 );
			hist[i][j] -> Draw( "hist" );
			canvas -> SaveAs( Form( "Plot/plot_%d_%d.png", i, j ) );
			delete canvas;
		}
	return 0;
}

Though the program runs fine, I still didn’t understanding my code well. When I remove the asterisk(s) in the malloc function:

TH1D** *hist = ( TH1D*** )malloc( sizeof(TH1D)*2 );

It still works, and the result looks ok, although I expected an seg fault from the missing asterisks.
So, I want to ask:

  1. Why the missing asterisk does not affect my result?
  2. Which is the correct way to define the array of histogram using malloc?

Thank you very much,
Hoa


ROOT Version: ROOT6.24/00
Platform: Ubuntu 18.04
Compiler: Not Provided


Hi @LongHoa,

I guess that you are referring to a couple of asterisks you removed in sizeof(TH1D**). The rest of the explanation will just assume that.
In the code above, the original line allocates enough space on the heap (via malloc()) to hold an array of 2 pointers. If you remove the those, you will allocate space for storing 2 TH1D instances. As long as the size of a TH1D object is greater than the size of a pointer -which it certainly is-, the allocation will be large enough for the expected usage. Therefore, in this case, you are reserving slightly more memory (while using just a few bytes at the beginning of the region).

Replied above; no expected segfault, as you are just using the first few bytes of a larger allocation; no unintended misuse or data corruption.

The code above is actually creating an array of pointers to arrays, which is not the same as a n-dimensional array in row-major (C) order.
I would simplify the code a lot -which also makes it more efficient-, by allocating space for N objects, and being N the total number of TH1D objects (i.e., a linear array).

Cheers,
J.

1 Like