Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

C/C++

Bulletproofing C++ Code


Step 1: Use Static Analysis

Figuring out a bug, or at least getting some understanding of the code's properties without executing it, has always held a great deal of significance for programmers. In fact, this is a cornerstone of code reviews—using a human brain to read and understand the code, looking for defects with a fresh eye. Peer code reviews remain the best approach for finding code defects. On average, 60 percent of defects can be removed via code reviews; see the "Software Defect Reduction Top 10 List" by Barry Boehm and Victor R. Basili (www.cebase.org/www/resources/reports/usc/usccse2001-515.pdf). This should not be surprising because code reviews use the finest analysis instrument—the human brain. A quest for code review automation, so that reviewers could concentrate on tasks of higher intelligence rather than validating conformance to coding conventions, drove the development of automated static-analysis tools, which has made tremendous strides in the last few years.

Properly implemented, automated static analysis effectively identifies the code's soft spots and possible bugs in a relatively short time—something that can't be achieved manually. For example, a commonly encountered error is failing to recognize that floating numbers cannot be compared with the "==" operator, unlike, say, integers or classes with implemented operator==; see the control expression for an if statement in Listing One.

bool SetClient::contains(double d, SetType & h)
 {
   for (SetType::const_iterator it = 
                 h.begin(); it != h.end(); ++it) {
        // Pointer to a double of value d?
        if (*((double*)((*it)->getValue())) == d) {
            return true;
        }
   }
   return false;
 }
Listing One

This is one of the more common checks supported by static-analysis tools. More sophisticated algorithms are required to spot data- or control-dependent errors, such as using NULL pointers to objects or files or locally leaking memory. For example, if Domino's failed to deliver a pizza, the following code's logic would no longer work, and the pizza consumption program would crash at the attempt to determine if the pizza was sliced:

Pizza* cheesePizza =  	Dominos::deliver();
if (!cheesePizza->sliced())
	Slice(cheesePizza);

More advanced static-analysis tools can analyze the source code of all functions involved (as long as they are available in source form) and will raise red flags for our pizza example that the cheesePizza pointer is dereferenced without checking if the deliver() function can return NULL under some conditions.

Two prevalent types of static analysis today are pattern matching and dataflow analysis. The pattern-matching approach supports implementation of a set of coding practices, or a coding policy. For C++, many such "best practices" are defined in books by Scott Meyers, Herb Sutter, and Andrei Alexandrescu; the "Gotchas" series by Dan Saks and Steve Dewhurst; de facto industry standards such as Ellemtel and MISRA; and common guidelines for 32- and 64-bit portability. In addition to supporting the common C++ guidelines, static-analysis tools should also provide the ability to customize rules to suit your specific application and implementation context, as well as help you identify code patterns indicative of application-specific defects.

Dataflow analysis is based on the idea that program code can be compiled into graph-like representations, which can be simulated to determine whether certain execution paths can (in principle) lead to structural bugs such as memory leaks or (the favorite bug of the Java pundits) array bounds errors. This bug-prevention technique is powerful because it does not depend on user input to identify bugs that, in reality, are data dependent. This technique can only find bugs in available source code (binary libraries will present the equivalent of an impenetrable fence), but this limitation does not detract from its high ROI.

Effective application of static analysis goes beyond buying the appropriate tools; it also requires the careful application and monitoring of processes. To start, the team architect (or other designated leader) defines a coding policy that team members understand and respect, then configures the tool to automatically check that coding policy. If, like most C++ development teams, you're charged with extending and maintaining large inherited code bases, the next step is typically to scan that code base and resolve all reported violations. This removes a significant number of hidden defects in the code you will be building upon, improving the stability of the foundation code.

After cleaning the violations in the legacy code, establish a process for when the team starts building upon that code base. Ideally, this involves implementing two levels of automated static analysis.

  • The first level occurs on the programmer's desktop. As programmers complete new or modified code, they perform static analysis from their IDE, then resolve all reported violations before committing the related code to source control.
  • The second level is performed from a team server machine, where batch-mode analysis automatically checks the entire code base each night, and violation counts are tracked and correlated with other application quality metrics.

As additional defects surface (reported by developers or testers) and are fixed, the team should not miss the opportunity to analyze their root causes and try to identify a set of rules that prevents the same bugs from recurring, then configure the static-analysis tool to automatically check the entire codebase against these rules.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.