22.2 Null Pointers
Table of Contents
Understanding Null Pointers in ROOT
Null pointers are one of the most common immediate causes of crashes in ROOT analysis. Recognizing them early and guarding against them will save you a lot of time and frustration.
What a Null Pointer Is in Practice
In C++ a pointer variable can either point to a valid object in memory or to "nothing." When it points to nothing, its value is nullptr (or 0 in older code). A null pointer is just a pointer that does not refer to any real object.
In ROOT-based analysis you often write code like
TH1F *h = (TH1F*)f->Get("hEnergy");
h->Draw();
If f->Get("hEnergy") cannot find the object, it returns a null pointer. The pointer h exists, but there is no histogram behind it. The next line tries to call Draw() on "nothing." This is undefined behavior and typically causes a segmentation fault.
Always assume any pointer returned by ROOT can be null.
Always check before you use it.
Typical Places Where Null Pointers Appear in ROOT
Null pointers usually come from ROOT calls that are supposed to return a pointer to an object, but do not find or create what you expect. Some very common sources are:
TFile::Openand file handling
TFile *f = TFile::Open("data.root");
If the file does not exist, is corrupted, or cannot be opened, f may be nullptr. Even if it is not null, f->IsZombie() may be true, which means the file is not usable.
TFile::GetandTDirectory::Get
TH1F *h = (TH1F*)f->Get("histName");
If "histName" is not found in the file or directory, the returned pointer is null.
TTree::Get,gDirectory->Get, and similar lookup methods
Any lookup by name can return null if the name is wrong or the object is not there.- Canvases, pads, and global pointers
Pointers likegPad,gDirectory, orgROOT->FindObject("something")can be null depending on context. For example,gPadis null if no canvas or pad is currently active. - TTrees and branches
TTree *t = (TTree*)f->Get("tree");
If there is no TTree called "tree" in the file, t is null. Similarly, branch pointers retrieved via GetBranch can be null if the branch name is wrong.
- Dynamic object creation that fails
In most usual ROOT usagenewwill either succeed or terminate the program, sonewreturning null is rare. More often, failures are from ROOT access functions, not memory allocation itself.
Recognizing Symptoms of Null Pointer Problems
Null pointer issues often look like generic ROOT crashes, but there are some recognizable patterns:
- Crash when calling a method on a pointer
The code compiles, runs, and crashes exactly on a line such as:
h->Draw();
t->GetEntry(i);
canvas->cd(1);In a debugger, the backtrace will often show the crash inside a method of the class you are calling. This is a classic symptom of a method call on a null pointer.
- Immediate crash after
GetorOpen
You call something like:
TFile *f = TFile::Open("data.root");
TH1F *h = (TH1F*)f->Get("h1");
h->Draw();
and the crash happens at h->Draw(). This is often because "h1" is not present, so h is null.
- Access to TTrees or branches fails
You might see:
t->SetBranchAddress("energy", &energy);
compile but then crash as soon as you call GetEntry. If t is null because the tree was not retrieved properly, any subsequent use of t is invalid.
- Random behavior depending on ROOT session state
Sometimes a macro "works" when you run other commands before it, but crashes when you run it in a fresh ROOT session. This can happen when your macro relies on global pointers likegPadorgDirectorythat are only non-null after you have drawn something or opened a file.
Checking for Null Pointers in ROOT Code
The main defense against null pointer problems is simple checks before you use a pointer.
Checking file pointers
Whenever you open a file, check both for null and for "zombie" state:
TFile *f = TFile::Open("data.root");
if (!f || f->IsZombie()) {
std::cerr << "Error: cannot open file data.root" << std::endl;
return;
}
Rule: Never use a TFile* without checking if (!f || f->IsZombie()) first.
Checking object retrieval from files and directories
When you use Get, always verify the result:
TH1F *h = nullptr;
f->GetObject("hEnergy", h); // type-safe version, recommended
if (!h) {
std::cerr << "Error: histogram hEnergy not found in file" << std::endl;
return;
}
h->Draw();
If you use Get directly:
TH1F *h = (TH1F*)f->Get("hEnergy");
if (!h) {
std::cerr << "Error: hEnergy not found" << std::endl;
return;
}
h->Draw();The same pattern applies to trees:
TTree *t = (TTree*)f->Get("tree");
if (!t) {
std::cerr << "Error: TTree 'tree' not found" << std::endl;
return;
}Checking trees and branches
Before you use branches:
TBranch *bEnergy = t->GetBranch("energy");
if (!bEnergy) {
std::cerr << "Error: branch 'energy' not found in tree" << std::endl;
return;
}
When using SetBranchAddress, also confirm the tree itself is valid:
if (!t) {
std::cerr << "Error: null TTree pointer" << std::endl;
return;
}
t->SetBranchAddress("energy", &energy);Checking global pointers like gPad and gDirectory
Always assume globals might be null:
if (!gPad) {
std::cerr << "Warning: no active pad, creating canvas" << std::endl;
TCanvas *c = new TCanvas("c", "c", 800, 600);
c->cd();
}Similarly, for directories:
if (!gDirectory) {
std::cerr << "Warning: no current directory" << std::endl;
} else {
gDirectory->ls();
}Debugging a Suspected Null Pointer Crash
When you suspect a null pointer problem in ROOT, a few simple steps often isolate it quickly.
Step 1: Identify the crashing line
If you run macros with ROOT's interpreter, the error message often shows the line number at which the crash occurs. If not, use std::cout or std::cerr prints before likely problem lines to see how far the code gets:
std::cout << "About to retrieve histogram" << std::endl;
TH1F *h = (TH1F*)f->Get("hEnergy");
std::cout << "Retrieved histogram, about to draw" << std::endl;
h->Draw();
If the second message never appears, the crash is on h->Draw().
Step 2: Print pointer values
Before calling methods, print the pointer:
std::cout << "h pointer = " << h << std::endl;
if (!h) {
std::cerr << "Error: h is null!" << std::endl;
}
Any pointer printed as 0x0 is null.
Step 3: Use simple conditional checks
Add explicit checks and early returns:
if (!h) {
std::cerr << "Histogram pointer is null, aborting Draw()" << std::endl;
return;
}
h->Draw();Once you confirm the pointer is null, you can track back to where it was set.
Step 4: Verify object names and file contents
Many null pointer problems are just name mismatches. Use the ROOT browser or TFile::ls() to inspect what is really in the file:
f->ls(); // lists keys in the file
t->Print(); // prints TTree structureCheck that your code uses exactly the names that appear there, including case and directory paths.
Common Null Pointer Pitfalls in ROOT
It helps to know a few recurring patterns so you can avoid them.
Using a pointer before assigning it
This can happen if you declare a pointer, but never assign it:
TH1F *h;
// forgot to assign h
h->Fill(1.0); // crash, h is uninitialized (often effectively null or invalid)
Always assign a pointer before using it. If you do not have an object yet, set it explicitly to nullptr and check:
TH1F *h = nullptr;
if (!h) {
std::cerr << "Error: h not initialized" << std::endl;
}Assuming `Get` always works
It is very easy to write:
TH1F *h = (TH1F*)f->Get("h1");
h->Draw();This is convenient in quick interactive work, but for analysis code that you want to re-run reliably, you should always check:
if (!h) {
std::cerr << "Error: h1 not found in file" << std::endl;
return;
}Using removed or out-of-scope objects
In more advanced code, a pointer can become invalid after the object is deleted or after a file is closed. For example:
TH1F *h = nullptr;
{
TFile f("data.root");
f.GetObject("hEnergy", h);
// f goes out of scope here and is closed
}
h->Draw(); // h points to an object in a closed file, behavior is undefined
Here h is not null, but still invalid. This is a different bug from null pointers, but it often produces similar crashes.
A simple rule of thumb is to avoid keeping pointers to objects owned by files or directories that will be closed or destroyed. If you must, clone the object so it lives independently:
TFile f("data.root");
TH1F *hOnFile = nullptr;
f.GetObject("hEnergy", hOnFile);
TH1F *h = (TH1F*)hOnFile->Clone("h_local");
// now h is owned by the current directory, not the fileSafer Coding Practices to Reduce Null Pointer Bugs
There are several habits that systematically reduce null pointer issues in ROOT analysis code.
Always initialize pointers
Initialize pointers when you declare them:
TH1F *h = nullptr;
TTree *t = nullptr;
TFile *f = nullptr;
This way, if you forget to assign them, checks like if (!h) behave predictably.
Group checks near where pointers are obtained
Immediately after you obtain a pointer from ROOT, check it and handle errors close to where they originate. For example:
TFile *f = TFile::Open("data.root");
if (!f || f->IsZombie()) {
std::cerr << "Cannot open data.root" << std::endl;
return;
}
TTree *t = nullptr;
f->GetObject("tree", t);
if (!t) {
std::cerr << "TTree 'tree' not found in data.root" << std::endl;
return;
}Add simple helper functions
You can write small helper functions to avoid repeating checks:
TFile* OpenFileChecked(const char* filename) {
TFile *f = TFile::Open(filename);
if (!f || f->IsZombie()) {
std::cerr << "Error opening file: " << filename << std::endl;
return nullptr;
}
return f;
}
TH1F* GetHistogramChecked(TFile* f, const char* name) {
if (!f) return nullptr;
TH1F *h = nullptr;
f->GetObject(name, h);
if (!h) {
std::cerr << "Histogram '" << name << "' not found in file" << std::endl;
}
return h;
}Use them like this:
TFile *f = OpenFileChecked("data.root");
if (!f) return;
TH1F *h = GetHistogramChecked(f, "hEnergy");
if (!h) return;
h->Draw();This pattern centralizes the checks and makes crashes less likely.
Summary
Null pointers in ROOT almost always come from missing files, missing objects, wrong names, or global pointers that are not set in the current context. They show up as crashes when you call a method on a pointer that is actually pointing to nothing.
You can avoid and diagnose most null pointer problems by:
- Initializing all pointer variables.
- Checking results of
TFile::Open,Get,GetObject,GetBranch, and similar functions. - Printing and inspecting pointers when crashes occur.
- Verifying file and object names using
ls(), the ROOT browser, orPrint().
With these habits, null pointer errors become rare and easy to fix instead of mysterious and time consuming.
Views: 13
KAHIBARO